Construire un détecteur de pièce pour les appareils IdO sur Mac OS
À propos de l'auteur
Doctorat en intelligence artificielle à l'Université de Californie à Berkeley, axé sur les petits réseaux neuronaux dans la perception, pour les véhicules autonomes. Grand fan de cheesecake, de corgis et de …
Pour en savoir plus sur Alvin …
Dans ce didacticiel, vous apprendrez à créer une application de bureau qui prédit la pièce dans laquelle vous vous trouvez en utilisant un algorithme d'apprentissage automatique simple: les moindres carrés. Le code peut s'appliquer à n'importe quelle plate-forme – nous ne fournirons que des instructions d'installation de dépendance pour Mac OSX.
Connaître la pièce dans laquelle vous vous trouvez permet différentes applications IoT – de l'allumage à la modification des chaînes de télévision. Alors, comment pouvons-nous détecter le moment où vous et votre téléphone êtes dans la cuisine, la chambre ou le salon? Avec le matériel de base d'aujourd'hui, il existe une multitude de possibilités:
Une solution consiste à équiper chaque pièce d'un périphérique Bluetooth . Une fois que votre téléphone est à portée d'un périphérique Bluetooth, votre téléphone saura dans quelle pièce il se trouve, en fonction du périphérique Bluetooth. Cependant, la maintenance d’un ensemble de périphériques Bluetooth représente une charge considérable – du remplacement des batteries au remplacement de périphériques défectueux. De plus, la proximité de l’appareil Bluetooth n’est pas toujours la solution: si vous êtes dans le salon, près du mur partagé avec la cuisine, vos appareils de cuisine ne doivent pas commencer à produire de la nourriture.
Une autre solution à utiliser le GPS . Cependant, gardez à l'esprit que le GPS fonctionne mal à l'intérieur, dans lequel la multitude de murs, autres signaux et autres obstacles font des ravages sur la précision du GPS.
Notre approche est de tirer parti de tous les réseaux WiFi – même ceux auxquels votre téléphone n'est pas connecté. Voici comment: considérer la force du WiFi A dans la cuisine; disons que c’est 5. Comme il ya un mur entre la cuisine et la chambre, on peut raisonnablement s’attendre à ce que la force du WiFi A dans la chambre soit différente; disons que c’est 2. Nous pouvons exploiter cette différence pour prédire dans quelle pièce nous nous trouvons. De plus, le réseau WiFi B de notre voisin ne peut être détecté que depuis le salon, mais il est effectivement invisible de la cuisine. Cela rend la prédiction encore plus facile. En résumé, la liste de tous les réseaux WiFi en gamme nous fournit une information abondante.
Cette méthode présente les avantages suivants:
- ne nécessitant pas plus de matériel;
- fonctionnant avec des signaux plus stables comme le WiFi;
- fonctionnant bien où les autres techniques telles que le GPS sont faibles.
Plus il y a de murs, mieux c'est, car plus les forces du réseau WiFi sont disparates, plus les salles sont faciles à classer. Vous allez créer une application de bureau simple qui collecte des données, apprend à partir des données et prédit la pièce dans laquelle vous vous trouvez à un moment donné.
Pour en savoir plus sur SmashingMag:
Prérequis
, vous aurez besoin d'un Mac OSX. Alors que le code peut s’appliquer à n’importe quelle plate-forme, nous ne fournirons que des instructions d’installation de dépendance pour Mac.
Étape 0: Configuration de l'environnement de travail
Votre application de bureau sera écrite en NodeJS. Cependant, pour tirer parti de bibliothèques de calcul plus efficaces comme numpy
le code de formation et de prédiction sera écrit en Python. Pour commencer, nous allons configurer vos environnements et installer des dépendances. Créez un nouveau répertoire pour héberger votre projet.
mkdir ~ / riot
Accédez au répertoire.
cd ~ / riot
Utilisez pip pour installer le gestionnaire d'environnement virtuel par défaut de Python.
sudo pip install virtualenv
Créer un environnement virtuel Python3.6 nommé riot
.
virtualenv riot --python = python3.6
Activer l'environnement virtuel.
source Riot / bin / activate
Votre invite est maintenant précédée de (émeute)
. Cela indique que nous sommes entrés avec succès dans l'environnement virtuel. Installez les paquetages suivants à l'aide de pip
:
numpy
: Une bibliothèque d'algèbre linéaire efficace-
scipy
: bibliothèque de calcul scientifique implémentant des modèles d'apprentissage automatique populaires [19659033] pip install numpy == 1.14.3 scipy
== 1.1.0
Avec la configuration du répertoire de travail, nous allons commencer avec une application de bureau qui enregistre tous les réseaux WiFi à portée. Ces enregistrements constitueront des données d'entraînement pour votre modèle d'apprentissage automatique. Une fois les données disponibles, vous écrirez un classificateur des moindres carrés, formé sur les signaux WiFi collectés précédemment. Enfin, nous utiliserons le modèle des moindres carrés pour prédire la pièce dans laquelle vous vous trouvez, sur la base des réseaux WiFi à portée.
Étape 1: Application de bureau initiale
Dans cette étape, nous allons créer une nouvelle application de bureau en utilisant Electron JS. Pour commencer, nous allons plutôt utiliser le gestionnaire de paquets Node
npm
et un utilitaire de téléchargementwget
.brew install npm wget
Pour commencer, nous allons créer un nouveau projet Node.
npm init
Vous êtes invité à indiquer le nom du package, puis le numéro de version. Appuyez sur
ENTER
pour accepter le nom par défaut deanti-émeute
et la version par défaut de1.0.0
.nom du package: (émeute) version: (1.0.0)
Ceci vous demande une description du projet. Ajoutez une description non vide que vous souhaitez. Ci-dessous, la description est
Détecteur de pièce
description: Détecteur de pièce
Vous êtes invité à indiquer le point d'entrée ou le fichier principal à partir duquel exécuter le projet. Entrez
app.js
.Point d'entrée: (index.js) app.js
Ceci vous invite à la commande de test
git repository
. Cliquez surENTER
pour ignorer ces champs pour le moment.commande de test: dépôt git:
Ceci vous invite à
mots clés
etauteur
. Remplissez les valeurs que vous souhaitez. Ci-dessous, nous utilisonsiot
wifi
pour les mots-clés et utiliserJohn Doe
pour l'auteur.mots-clés: wifi auteur: John Doe
Vous êtes invité à entrer la licence. Hit
ENTER
pour accepter la valeur par défaut deISC
.licence: (ISC)
A ce stade,
npm
vous demandera un résumé des informations à ce jour. Votre sortie doit être similaire à la suivante.{ "nom": "émeute", "version": "1.0.0", "description": "détecteur de pièce", "main": "app.js", "scripts": { "test": "echo " Erreur: aucun test spécifié "&& exit 1" }, "mots-clés": [ "iot", "wifi" ], "auteur": "John Doe", "licence": "ISC" }
Hit
ENTER
pour accepter.npm
produit alors unpackage.json
. Lister tous les fichiers à vérifier.ls
Cela affichera le seul fichier dans ce répertoire, ainsi que le dossier de l'environnement virtuel.
package.json émeute
Installez les dépendances NodeJS pour notre projet.
npm install electron --global # rend les binaires électroniques accessibles globalement npm installer node-wifi --save
Commencez avec
main.js
à partir d'Electron Quick Start en téléchargeant le fichier en utilisant ce qui suit. L'argument-O
suivant renommemain.js
enapp.js
.wget https://raw.githubusercontent.com/electron/electron- démarrage rapide / master / main.js -O app.js
Ouvrez
app.js
dansnano
ou votre éditeur de texte préféré.nano app.js
Sur la ligne 12, remplacez index.html par static / index.html car nous allons créer un répertoire
static
pour contenir tous les modèles HTML. 19659021] function createWindow () {
// Crée la fenêtre du navigateur.
win = new BrowserWindow ({width: 1200, height: 800})// et charge le index.html de l'application.
win.loadFile ('static / index.html')// Ouvre les DevTools.
Enregistrez vos modifications et quittez l'éditeur. Votre fichier doit correspondre au code source du fichier
app.js
. Maintenant, créez un nouveau répertoire pour héberger nos modèles HTML.mkdir static
Téléchargez une feuille de style créée pour ce projet.
wget https://raw.githubusercontent.com/alvinwan/riot/master/static/style.css?token=AB-ObfDtD46ANlqrObDanckTQJ2Q1Pyuks5bf79PwA%3D%3D -O static / style.css
Ouvrir
static / index.html
dansnano
ou votre éditeur de texte préféré. Commencez avec la structure HTML standard.Riot | Détecteur de pièce Juste après le titre, liez la police Montserrat liée par Google Fonts et la feuille de style.
Riot | Détecteur de pièce Entre les balises
principale
ajoutez un emplacement pour le nom de pièce prédit.Je crois que vous êtes dans le
(I dunno)
Votre script devrait maintenant correspondre exactement à ce qui suit. Quittez l'éditeur.
Riot | Détecteur de pièce Je crois que vous êtes dans le
(I dunno)
Modifiez maintenant le fichier de package pour qu'il contienne une commande de démarrage.
nano package.json
Juste après la ligne 7, ajouter une commande de
start
qui est aliasée àelectron.
. Veillez à ajouter une virgule à la fin de la ligne précédente."scripts": { "test": "echo " Erreur: aucun test spécifié "&& exit 1", "commencer": "électron" },
Enregistrez et quittez. Vous êtes maintenant prêt à lancer votre application de bureau dans Electron JS. Utilisez
npm
pour lancer votre application.npm start
Votre application de bureau doit correspondre à la suivante:
Page d'accueil avec le bouton "Ajouter une nouvelle pièce" disponible ( Grand aperçu ) Ceci complète votre application de bureau de démarrage. Pour quitter, retournez à votre terminal et CTRL + C. À l'étape suivante, nous enregistrerons les réseaux wifi et rendrons l'utilitaire d'enregistrement accessible via l'interface utilisateur de l'application de bureau.
Étape 2: Enregistrement de réseaux WiFi
Dans cette étape, vous allez écrire un script NodeJS fréquence de tous les réseaux wifi dans la gamme. Créez un répertoire pour vos scripts.
scripts mkdir
Ouvrez
scripts / observe.js
dansnano
ou votre éditeur de texte préféré.nano scripts / observe.js
Importer un utilitaire wifi NodeJS et l'objet système de fichiers.
var wifi = require ('node-wifi'); var fs = require ('fs');
Définir une fonction d'enregistrement
/ ** * Utilise une fonction récursive pour les analyses répétées, car les analyses sont asynchrones. * / enregistrement de fonction (n, complétion, hook) { }
Dans la nouvelle fonction, initialisez l'utilitaire wifi. Définissez
iface
sur null pour l'initialiser sur une interface wifi aléatoire, car cette valeur n'est pas pertinente pour le moment.enregistrement de fonction (n, completion, hook) { wifi.init ({ iface: null }) }
Définissez un tableau pour contenir vos échantillons. Les échantillons sont des données d'entraînement que nous utiliserons pour notre modèle. Les exemples de ce tutoriel sont des listes de réseaux Wi-Fi de gamme et leurs forces, fréquences, noms, etc. associés.
enregistrement de fonction (n, complétion, hook) { ... échantillons = [] }
Définissez une fonction récursive
startScan
qui lancera de manière asynchrone des analyses wifi. Une fois terminé, le scan asynchrone wifi invoquera récursivementstartScan
.enregistrement de fonction (n, achèvement, hook) { ... fonction startScan (i) { wifi.scan (fonction (err, réseaux) { }) } startScan (n); }
Dans le rappel
wifi.scan
vérifiez les erreurs ou les listes de réseaux vides et redémarrez l'analyse si c'est le cas.wifi.scan (fonction (err, networks) { if (err || networks.length == 0) { startScan (i); revenir } })
Ajoutez le cas de base de la fonction récursive, qui appelle le gestionnaire d'achèvement.
wifi.scan (function (err, networks) { ... if (i
Affiche une mise à jour de la progression, ajoute à la liste des échantillons et effectue l'appel récursif.
wifi.scan (function (err, networks) { ... crochet (n-i + 1, réseaux); samples.push (réseaux); startScan (i-1); })
À la fin de votre fichier, appelez la fonction
record
avec un rappel qui enregistre les échantillons dans un fichier sur le disque.enregistrement de fonction (achèvement) { ... } fonction cli () { enregistrement (1, fonction (données) { fs.writeFile ('samples.json', JSON.stringify (data), 'utf8', function () {}); }, fonction (i, réseaux) { console.log ("* [INFO] Échantillon collecté" + (21-i) + "avec" + networks.length + "networks"); }) } cli ();
Vérifiez que votre fichier correspond à ce qui suit:
var wifi = require ('node-wifi'); var fs = require ('fs'); / ** * Utilise une fonction récursive pour les analyses répétées, car les analyses sont asynchrones. * / enregistrement de fonction (n, complétion, hook) { wifi.init ({ iface: null // interface réseau, choisissez une interface wifi aléatoire si elle est définie sur null }) échantillons = [] fonction startScan (i) { wifi.scan (fonction (err, réseaux) { if (err || networks.length == 0) { startScan (i); revenir } if (i
Enregistrer et quitter. Exécuter le script.
node scripts / observe.js
Votre sortie correspondra aux éléments suivants, avec un nombre variable de réseaux.
* [INFO] Échantillon recueilli 1 avec 39 réseaux
Examinez les échantillons qui viennent d'être recueillis. Pipe to
json_pp
pour imprimer assez le JSON et le pipe to head pour voir les 16 premières lignes.cat samples.json | json_pp | tête -16
L'exemple ci-dessous est un exemple de sortie pour un réseau 2,4 GHz.
{ "samples": [ [ { "mac": "64:0f:28:79:9a:29", "bssid": "64:0f:28:79:9a:29", "ssid": "SMASHINGMAGAZINEROCKS", "channel": 4, "frequency": 2427, "signal_level": "-91", "security": "WPA WPA2", "security_flags": [ "(PSK/AES,TKIP/TKIP)", "(PSK/AES,TKIP/TKIP)" ] },
Ceci conclut votre script de numérisation wifi NodeJS. Cela nous permet de voir tous les réseaux WiFi dans la gamme. Dans l'étape suivante, vous rendrez ce script accessible depuis l'application de bureau.
Étape 3: Connectez le script de numérisation à l'application de bureau
Dans cette étape, vous ajouterez d'abord un bouton à l'application de bureau pour déclencher le script avec . Ensuite, vous mettrez à jour l'interface utilisateur du bureau avec la progression du script.
Ouvrir
static / index.html
.nano static / index.html
Insérer le bouton "Ajouter", comme illustré ci-dessous.
(I dunno)
Enregistrez et quittez. Ouvrez
static / add.html
.nano static / add.html
Collez le contenu suivant.
Riot | Ajouter une nouvelle pièce 0
de 20 échantillons nécessaires. N'hésitez pas à vous déplacer dans la pièce.
Enregistrez et quittez. Rouvrez
scripts / observe.js
.nano scripts / observe.js
Sous la fonction
cli
définissez une nouvelle fonctionui
.function cli () { ... } // commence le nouveau code fonction ui () { } // met fin au nouveau code cli ();
Mettez à jour l'état de l'application de bureau pour indiquer que la fonction a commencé à s'exécuter.
function ui () { var room_name = document.querySelector ('# add-room-name'). valeur; var status = document.querySelector ('# add-status'); var number = document.querySelector ('# add-title'); status.style.display = "bloquer" status.innerHTML = "Ecouter le wifi ..." }
Partitionnez les données en jeux de données d'entraînement et de validation.
function ui () { ... achèvement de la fonction (données) { train_data = {samples: data ['samples'] .slice (0, 15)} test_data = {samples: data ['samples'] .slice (15)} var train_json = JSON.stringify (train_data); var test_json = JSON.stringify (test_data); } }
Toujours dans le rappel
écrivez les deux jeux de données sur le disque.
function ui () { ... achèvement de la fonction (données) { ... fs.writeFile ('data /' + room_name + '_train.json', train_json, 'utf8', function () {}); fs.writeFile ('data /' + room_name + '_test.json', test_json, 'utf8', function () {}); console.log ("* [INFO] Terminé") status.innerHTML = "Fait" } }
Appelez l'enregistrement
function ui () { ... achèvement de la fonction (données) { ... } enregistrement (20, achèvement, fonction (i, réseaux) { number.innerHTML = i console.log ("* [INFO] Exemple de collecte" + i + "avec" + networks.length + "networks") }) }
Enfin, invoquer les fonctions
cli
etui
le cas échéant. Commencez par supprimer l'appelcli ();
au bas du fichier.function ui () { ... } cli (); // me retirer
Vérifiez si l'objet du document est accessible globalement. Sinon, le script est exécuté à partir de la ligne de commande. Dans ce cas, appelez la fonction
cli
. Si c'est le cas, le script est chargé depuis l'application de bureau. Dans ce cas, associez l'écouteur de clic à la fonctionui
.if (typeof document == 'undefined') { cli (); } autre { document.querySelector ('# start-recording'). addEventListener ('click', ui) }
Enregistrez et quittez. Créez un répertoire contenant nos données.
mkdir data
Lancez l'application de bureau.
npm start
Vous verrez la page d'accueil suivante. Cliquez sur "Ajouter une pièce".
( Grand aperçu ) Vous verrez le formulaire suivant. Tapez un nom pour la salle. Rappelez-vous ce nom, comme nous l'utiliserons plus tard. Notre exemple sera
chambre à coucher
.
Page "Ajouter une nouvelle salle" à charger ( Grand aperçu ) Cliquez sur "Démarrer l'enregistrement" et vous verrez le statut suivant "Ecoute pour le wifi ...".
"Ajouter une nouvelle salle" pour lancer l'enregistrement ( Grand aperçu ) Une fois que tous les 20 échantillons sont enregistrés, votre application correspondra à ce qui suit. L'état indiquera "Terminé".
La page "Ajouter une nouvelle pièce" après l'enregistrement est terminée ( Grand aperçu ) Cliquez sur le lien "Annuler" pour revenir à la page d'accueil, qui correspond à la suivante.
La page "Ajouter une pièce" après l'enregistrement est terminée ( Grand aperçu ) Nous pouvons maintenant analyser les réseaux wifi à partir de l'interface utilisateur du bureau, ce qui permettra d'enregistrer tous les échantillons enregistrés dans des fichiers sur le disque. Ensuite, nous allons former un algorithme d'apprentissage automatique prêt à l'emploi - les moindres carrés sur les données que vous avez collectées.
Étape 4: Ecrire un script de formation Python
Créez un répertoire pour vos utilitaires de formation.
Modèle mkdir
Open
model / train.py
nano model / train.py
En haut de votre fichier, importez la bibliothèque de calcul
numpy
etscipy
pour son modèle des moindres carrés.import numpy as np à partir de scipy.linalg import lstsq importer json import sys
Les trois utilitaires suivants gèrent le chargement et la configuration des données à partir des fichiers sur le disque. Commencez par ajouter une fonction utilitaire qui aplatit les listes imbriquées. Vous allez l'utiliser pour aplatir une liste de liste d'échantillons.
import sys def flatten (list_of_lists): "" "Aplatir une liste de listes pour en faire une liste. >>> aplatir ([[1][2][3, 4]]) [1, 2, 3, 4] "" " somme de retour (list_of_lists, [])
Ajoutez un deuxième utilitaire qui charge des exemples à partir des fichiers spécifiés. Cette méthode fait abstraction du fait que les échantillons sont répartis sur plusieurs fichiers, ne renvoyant qu’un seul générateur pour tous les échantillons. Pour chacun des échantillons, l'étiquette est l'index du fichier. Par exemple, si vous appelez
get_all_samples ('a.json', 'b.json')
tous les échantillons dea.json
porteront l'étiquette 0 et tous les échantillons deb .json
aura le label 1.def get_all_samples (chemins): "" "Charger tous les échantillons des fichiers JSON." "" pour label, chemin en enumerate (chemins): avec open (path) comme f: pour un exemple dans json.load (f) ['samples']: signal_levels = [ network['signal_level'] .replace ('RSSI', '') ou 0 pour le réseau dans l'échantillon] rendement [network['mac'] pour le réseau dans l'échantillon]signal_levels, label
Ensuite, ajoutez un utilitaire qui code les échantillons à l'aide d'un modèle de type bag-of-words. Voici un exemple: Supposons que nous collections deux échantillons.
- Réseau wifi A à la puissance 10 et réseau wifi B à la puissance 15
- Réseau wifi B à la puissance 20 et réseau wifi C à la puissance 25.
produire une liste de trois nombres pour chacun des échantillons: la première valeur est la force du réseau wifi A, la deuxième pour le réseau B et la troisième pour C. En fait, le format est [A, B, C].
- [10, 15, 0]
- [0, 20, 25]
def bag_of_words (all_networks, all_strengths, ordering): Msgstr "" "Appliquer le codage des mots de passe aux variables catégorielles. >>> samples = bag_of_words ( ... [['a', 'b']['b', 'c']['a', 'c']], ... [[1, 2][2, 3][1, 3]], ... ['a', 'b', 'c']) >>> suivant (échantillons) [1, 2, 0] >>> suivant (échantillons) [0, 2, 3] "" " pour les réseaux, les points forts de zip (all_networks, all_strengths): rendement [strengths[networks.index(network)] si le réseau dans les réseaux sinon 0 pour le réseau dans la commande]
En utilisant les trois utilitaires ci-dessus, nous synthétisons une collection d'échantillons et leurs étiquettes. Rassemblez tous les échantillons et étiquettes en utilisant
get_all_samples
. Définissez un format cohérentordonnant
à coder tous les échantillons à chaud, puis appliquez le codageone_hot
aux échantillons. Enfin, construisez respectivement les matrices de données et d'étiquettesX
etY
.def create_dataset (classpaths, ordering = None): "" "Créer un ensemble de données à partir d'une liste de chemins d'accès aux fichiers JSON." "" réseaux, forces, labels = zip (* get_all_samples (classpaths)) si la commande est Aucune: ordering = list (trié (set (aplatir (réseaux)))) X = np.array (liste (bag_of_words (réseaux, forces, classement))). Astype (np.float64) Y = np.array (liste (labels)). Astype (np.int) retourner X, Y, ordonner
Ces fonctions complètent le pipeline de données. Ensuite, nous décrivons la prédiction et l'évaluation des modèles éloignés. Commencez par définir la méthode de prédiction. La première fonction normalise les sorties de notre modèle, de sorte que la somme de toutes les valeurs s’élève à 1 et que toutes les valeurs sont non négatives; Cela garantit que la sortie est une distribution de probabilité valide. La seconde évalue le modèle.
def softmax (x): "" Convertir les sorties à un point en distribution de probabilité "" " x = np.exp (x) return x / np.sum (x) def prédire (X, w): "" "Prédire en utilisant les paramètres du modèle" "" return np.argmax (softmax (X.dot (w)), axis = 1)
Ensuite, évaluez la précision du modèle. La première ligne exécute la prédiction en utilisant le modèle. La seconde compte les nombres de fois où les valeurs prédites et vraies sont en accord, puis se normalise par le nombre total d'échantillons.
def evaluation (X, Y, w): "" "Évaluez le modèle W sur les échantillons X et les étiquettes Y." "" Y_pred = prédire (X, w) précision = (Y == Y_pred) .sum () / X.shape [0] précision de retour
Ceci conclut nos utilitaires de prévision et d'évaluation. Après ces utilitaires, définissez une fonction
principale
qui collectera le jeu de données, formera et évaluera. Commencez par lire la liste des arguments de la ligne de commandesys.argv
; Ce sont les salles à inclure dans la formation. Créez ensuite un ensemble de données volumineux à partir de toutes les pièces spécifiées.def main (): classes = sys.argv [1:] train_paths = trié (['data/{}_train.json'.format(name) for name in classes]) chemins_test = classés (['data/{}_test.json'.format(name) for name in classes]) X_train, Y_train, ordering = create_dataset (train_paths) X_test, Y_test, _ = create_dataset (chemins_test, commande = commande)
Appliquez un encodage à chaud aux étiquettes. Un codage à chaud unique est similaire au modèle ci-dessus contenant des mots-clés; nous utilisons cet encodage pour gérer les variables catégorielles. Disons que nous avons 3 étiquettes possibles. Au lieu d'étiqueter 1, 2 ou 3, nous étiquetons les données avec [1, 0, 0][0, 1, 0] ou [0, 0, 1]. Pour ce tutoriel, nous expliquerons pourquoi l’encodage à un seul point est important. Entraînez le modèle et évaluez à la fois le train et les ensembles de validation.
def main (): ... X_test, Y_test, _ = create_dataset (chemins_test, commande = commande) Y_train_oh = np.eye (len (classes)) [Y_train] w, _, _, _ = lstsq (X_train, Y_train_oh) train_accuracy = évaluer (X_train, Y_train, w) test_accuracy = évaluer (X_test, Y_test, w)
Imprimez les deux précisions et enregistrez le modèle sur le disque.
def main (): ... print ('Précision du train ({}%), Précision de la validation ({}%)'. format (train_accuracy * 100, test_accuracy * 100)) np.save ('w.npy', w) np.save ('ordering.npy', np.array (commande)) sys.stdout.flush ()
A la fin du fichier, exécutez la fonction
main
.si __name__ == '__main__': principale()
Enregistrez et quittez. Vérifiez que votre fichier correspond aux éléments suivants:
import numpy as np à partir de scipy.linalg import lstsq importer json import sys def flatten (list_of_lists): "" "Aplatir une liste de listes pour en faire une liste. >>> aplatir ([[1][2][3, 4]]) [1, 2, 3, 4] "" " somme de retour (list_of_lists, []) def get_all_samples (chemins): "" "Charger tous les échantillons des fichiers JSON." "" pour label, chemin en enumerate (chemins): avec open (path) comme f: pour un exemple dans json.load (f) ['samples']: signal_levels = [ network['signal_level'] .replace ('RSSI', '') ou 0 pour le réseau dans l'échantillon] rendement [network['mac'] pour le réseau dans l'échantillon]signal_levels, label def bag_of_words (all_networks, all_strengths, ordering): Msgstr "" "Appliquer le codage des mots de passe aux variables catégorielles. >>> samples = bag_of_words ( ... [['a', 'b']['b', 'c']['a', 'c']], ... [[1, 2][2, 3][1, 3]], ... ['a', 'b', 'c']) >>> suivant (échantillons) [1, 2, 0] >>> suivant (échantillons) [0, 2, 3] "" " pour les réseaux, les points forts de zip (all_networks, all_strengths): rendement [int(strengths[networks.index(network)]) si le réseau dans les réseaux sinon 0 pour le réseau dans la commande] def create_dataset (classpaths, ordering = None): "" "Créer un ensemble de données à partir d'une liste de chemins d'accès aux fichiers JSON." "" réseaux, forces, labels = zip (* get_all_samples (classpaths)) si la commande est Aucune: ordering = list (trié (set (aplatir (réseaux)))) X = np.array (liste (bag_of_words (réseaux, forces, classement))). Astype (np.float64) Y = np.array (liste (labels)). Astype (np.int) retourner X, Y, ordonner def softmax (x): "" Convertir les sorties à un point en distribution de probabilité "" " x = np.exp (x) return x / np.sum (x) def prédire (X, w): "" "Prédire en utilisant les paramètres du modèle" "" return np.argmax (softmax (X.dot (w)), axis = 1) def évaluer (X, Y, w): "" "Évaluez le modèle W sur les échantillons X et les étiquettes Y." "" Y_pred = prédire (X, w) précision = (Y == Y_pred) .sum () / X.shape [0] précision de retour def main (): classes = sys.argv [1:] train_paths = trié (['data/{}_train.json'.format(name) for name in classes]) chemins_test = classés (['data/{}_test.json'.format(name) for name in classes]) X_train, Y_train, ordering = create_dataset (train_paths) X_test, Y_test, _ = create_dataset (chemins_test, commande = commande) Y_train_oh = np.eye (len (classes)) [Y_train] w, _, _, _ = lstsq (X_train, Y_train_oh) train_accuracy = évaluer (X_train, Y_train, w) validation_accurat = évaluer (X_test, Y_test, w) print ('Précision du train ({}%), Précision de la validation ({}%)'. format (train_accuracy * 100, validation_accure * 100)) np.save ('w.npy', w) np.save ('ordering.npy', np.array (commande)) sys.stdout.flush () si __name__ == '__main__': principale()
Enregistrez et quittez. Rappelez-vous le nom de la pièce utilisé ci-dessus lors de l'enregistrement des 20 échantillons. Utilisez ce nom au lieu de
bedroom
ci-dessous. Notre exemple estchambre à coucher
. Nous utilisons-W ignore
pour ignorer les avertissements d'un bogue LAPACK.python -W ignore le modèle / train.py
Comme nous n'avons collecté que des échantillons de formation pour une salle, vous devriez voir une précision de 100% pour l'entraînement et la validation.
Précision du train (100,0%), précision de la validation (100,0%)
Ensuite, nous lierons ce script de formation à l'application de bureau.
Étape 5: Script de train de liaison
Dans cette étape, nous reconditionnerons automatiquement le modèle chaque fois que l'utilisateur collecte un nouveau lot d'échantillons. Ouvrez
scripts / observe.js
.nano scripts / observe.js
Juste après l'importation de
fs
importez le générateur de processus enfant et les utilitaires.var fs = require ('fs'); // commence le nouveau code const spawn = require ("child_process"). var utils = require ('./ utils.js');
Dans la fonction
ui
ajoutez l'appel suivant àretrain
à la fin du gestionnaire d'achèvement.function ui () { ... achèvement de la fonction () { ... recycler ((données) => { var status = document.querySelector ('# add-status'); Précision = data.toString (). split (' n') [0]; status.innerHTML = "Réorganisation réussie:" + précision }) } ... }
Après la fonction
ui
ajouter la fonction de reconditionnementsuivante
. Cela génère un processus enfant qui exécutera le script python. Une fois terminé, le processus appelle un gestionnaire d'achèvement. En cas d'échec, il enregistrera le message d'erreur.function ui () { .. } reconditionnement de la fonction (achèvement) { var noms de fichiers = utils.get_filenames () const pythonProcess = spawn ('python', ["./model/train.py"] .concat (noms de fichiers)); pythonProcess.stdout.on ('data', accomplissement); pythonProcess.stderr.on ('data', (data) => { console.log ("* [ERROR]" + data.toString ()) }) }
Enregistrez et quittez. Ouvrez
scripts / utils.js
.nano scripts / utils.js
Ajoutez l'utilitaire suivant pour récupérer tous les jeux de données dans
data /
.var fs = require ('fs'); module.exports = { get_filenames: get_filenames } function get_filenames () { noms de fichiers = nouvel ensemble ([]); fs.readdirSync ("data /"). forEach (function (filename) { nomsfichiers.add (filename.replace ('_ train', '') .replace ('_ test', '') .replace ('. json', '')) }) noms de fichiers = Array.from (nomsfichiers.values ()) noms de fichier.sort (); nomsfichiers.splice (nomsfichiers.indexOf ('. DS_Store'), 1) renvoyer les noms de fichiers }
Enregistrez et quittez. Pour terminer cette étape, déplacez-vous physiquement vers un nouvel emplacement. Il devrait idéalement être un mur entre votre emplacement d'origine et votre nouvel emplacement. Plus les obstacles sont nombreux, plus votre application de bureau fonctionnera.
Exécutez à nouveau votre application de bureau.
npm start
Comme précédemment, lancez le script de formation. Cliquez sur "Ajouter une pièce".
Page d'accueil avec le bouton "Ajouter une nouvelle pièce" disponible ( Grand aperçu ) Saisissez un nom de chambre différent de celui de votre première pièce. Nous utiliserons
salon
.
Page "Ajouter une nouvelle pièce" ( Grand aperçu ) Click “Start recording,” and you will see the following status “Listening for wifi…”.
“Add New Room” starting recording for second room (Large preview) Once all 20 samples are recorded, your app will match the following. The status will read “Done. Retraining model…”
“Add New Room” page after recording for second room complete (Large preview) In the next step, we will use this retrained model to predict the room you’re in, on the fly.
Step 6: Write Python Evaluation Script
In this step, we will load the pretrained model parameters, scan for wifi networks, and predict the room based on the scan.
Open
model/eval.py
.nano model/eval.py
Import libraries used and defined in our last script.
import numpy as np import sys import json import os import json from train import predict from train import softmax from train import create_dataset from train import evaluate
Define a utility to extract the names of all datasets. This function assumes that all datasets are stored in
data/
as
and_train.json
._test.json from train import evaluate def get_datasets(): """Extract dataset names.""" return sorted(list({path.split('_')[0] for path in os.listdir('./data') if '.DS' not in path}))
Define the
main
function, and start by loading parameters saved from the training script.def get_datasets(): ... def main(): w = np.load('w.npy') ordering = np.load('ordering.npy')
Create the dataset and predict.
def main(): ... classpaths = [sys.argv[1]] X, _, _ = create_dataset(classpaths, ordering) y = np.asscalar(predict(X, w))
Compute a confidence score based on the difference between the top two probabilities.
def main(): ... sorted_y = sorted(softmax(X.dot(w)).flatten()) confidence = 1 if len(sorted_y) > 1: confidence = round(sorted_y[-1] - sorted_y[-2]2)
Finally, extract the category and print the result. To conclude the script, invoke the
main
function.def main() ... category = get_datasets()[y] print(json.dumps({"category": category, "confidence": confidence})) if __name__ == '__main__': main()
Save and exit. Double check your code matches the following (source code):
import numpy as np import sys import json import os import json from train import predict from train import softmax from train import create_dataset from train import evaluate def get_datasets(): """Extract dataset names.""" return sorted(list({path.split('_')[0] for path in os.listdir('./data') if '.DS' not in path})) def main(): w = np.load('w.npy') ordering = np.load('ordering.npy') classpaths = [sys.argv[1]] X, _, _ = create_dataset(classpaths, ordering) y = np.asscalar(predict(X, w)) sorted_y = sorted(softmax(X.dot(w)).flatten()) confidence = 1 if len(sorted_y) > 1: confidence = round(sorted_y[-1] - sorted_y[-2]2) category = get_datasets()[y] print(json.dumps({"category": category, "confidence": confidence})) if __name__ == '__main__': main()
Next, we will connect this evaluation script to the desktop app. The desktop app will continuously run wifi scans and update the UI with the predicted room.
Step 7: Connect Evaluation To Desktop App
In this step, we will update the UI with a “confidence” display. Then, the associated NodeJS script will continuously run scans and predictions, updating the UI accordingly.
Open
static/index.html
.nano static/index.html
Add a line for confidence right after the title and before the buttons.
(I dunno)
with 0% confidence