Mac OS'de IoT Cihazları İçin Bir Oda Dedektörü Oluşturma

Yayınlanan: 2022-03-10
Hızlı özet ↬ Bu öğreticide, basit bir makine öğrenimi algoritması kullanarak hangi odada olduğunuzu tahmin eden bir masaüstü uygulaması oluşturacaksınız: en küçük kareler. Kod herhangi bir platform için geçerlidir, ancak yalnızca Mac OSX için bağımlılık yükleme yönergeleri sağlarız.

Hangi odada olduğunuzu bilmek, ışığı açmaktan TV kanallarını değiştirmeye kadar çeşitli IoT uygulamalarına olanak tanır. Peki sizin ve telefonunuzun mutfakta, yatak odasında veya oturma odasında olduğunuz anı nasıl tespit edebiliriz? Günümüzün emtia donanımı ile sayısız olasılık var:

Bir çözüm, her odayı bir bluetooth cihazı ile donatmaktır . Telefonunuz bir bluetooth cihazının kapsama alanına girdiğinde, telefonunuz bluetooth cihazına bağlı olarak hangi oda olduğunu bilecektir. Bununla birlikte, bir dizi Bluetooth cihazının bakımı, pillerin değiştirilmesinden işlevsiz cihazların değiştirilmesine kadar önemli bir ek yüktür. Ek olarak, Bluetooth cihazına yakın olmak her zaman çözüm değildir: oturma odasında, mutfakla paylaşılan duvarın yanındaysanız, mutfak aletleriniz yiyecek yaymaya başlamamalıdır.

Pratik olmasa da başka bir çözüm de GPS kullanmaktır . Ancak, GPS'in çok sayıda duvarın, diğer sinyallerin ve diğer engellerin GPS'in hassasiyetine zarar verdiği iç mekanlarda yetersiz çalıştığını unutmayın.

Bunun yerine yaklaşımımız, telefonunuzun bağlı olmadığı ağlar bile, tüm menzil içi WiFi ağlarından yararlanmaktır. İşte nasıl: mutfakta WiFi A'nın gücünü düşünün; 5 olduğunu söyleyin. Mutfak ile yatak odası arasında bir duvar olduğundan, yatak odasında WiFi A'nın gücünün farklı olmasını makul bir şekilde bekleyebiliriz; 2 olduğunu söyle. Hangi odada olduğumuzu tahmin etmek için bu farkı kullanabiliriz. Dahası: Komşumuzdan gelen WiFi ağı B sadece oturma odasından algılanabilir, ancak mutfaktan fiilen görünmez. Bu, tahmini daha da kolaylaştırır. Özetle, tüm menzil içi WiFi listesi bize bol miktarda bilgi verir.

Bu yöntemin belirgin avantajları vardır:

  1. daha fazla donanım gerektirmeyen;
  2. WiFi gibi daha kararlı sinyallere güvenmek;
  3. GPS gibi diğer tekniklerin zayıf olduğu yerlerde iyi çalışıyor.

Duvarlar ne kadar çok olursa, WiFi ağının güçleri ne kadar farklı olursa, odaların sınıflandırılması o kadar kolay olur. Veri toplayan, verilerden öğrenen ve herhangi bir zamanda hangi odada olduğunuzu tahmin eden basit bir masaüstü uygulaması oluşturacaksınız.

SmashingMag'de Daha Fazla Okuma :

  • Akıllı Konuşma Kullanıcı Arayüzü Yükselişi
  • Tasarımcılar İçin Makine Öğrenimi Uygulamaları
  • IoT Deneyimlerinin Prototipi Nasıl Yapılır: Donanımı İnşa Etme
  • Duygusal Şeylerin İnterneti İçin Tasarım

Önkoşullar

Bu eğitim için bir Mac OSX'e ihtiyacınız olacak. Kod herhangi bir platforma uygulanabilirken, yalnızca Mac için bağımlılık kurulum talimatlarını sağlayacağız.

  • Mac OS X
  • Homebrew, Mac OSX için bir paket yöneticisi. Yüklemek için brew.sh adresindeki komutu kopyalayıp yapıştırın
  • NodeJS 10.8.0+ ve npm kurulumu
  • Python 3.6+ ve pip kurulumu. Virtualenv Nasıl Kurulur, pip ile Kurulum ve Paketleri Yönetme konusunun ilk 3 bölümüne bakın.
Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Adım 0: Çalışma Ortamını Kurun

Masaüstü uygulamanız NodeJS'de yazılacaktır. Ancak, numpy gibi daha verimli hesaplama kitaplıklarından yararlanmak için eğitim ve tahmin kodu Python'da yazılacaktır. Başlamak için ortamlarınızı ayarlayacağız ve bağımlılıkları kuracağız. Projenizi barındırmak için yeni bir dizin oluşturun.

 mkdir ~/riot

Dizine gidin.

 cd ~/riot

Python'un varsayılan sanal ortam yöneticisini kurmak için pip kullanın.

 sudo pip install virtualenv

riot adlı bir Python3.6 sanal ortamı oluşturun.

 virtualenv riot --python=python3.6

Sanal ortamı etkinleştirin.

 source riot/bin/activate

İsteminizin önüne artık (riot) . Bu, sanal ortama başarıyla girdiğimizi gösterir. pip kullanarak aşağıdaki paketleri kurun:

  • numpy : Verimli, doğrusal bir cebir kitaplığı
  • scipy : Popüler makine öğrenimi modellerini uygulayan bilimsel bir bilgi işlem kitaplığı
 pip install numpy==1.14.3 scipy ==1.1.0

Çalışma dizini kurulumuyla, menzil içindeki tüm WiFi ağlarını kaydeden bir masaüstü uygulamasıyla başlayacağız. Bu kayıtlar, makine öğrenimi modeliniz için eğitim verilerini oluşturacaktır. Elimizde veri olduğunda, daha önce toplanan WiFi sinyallerine göre eğitilmiş bir en küçük kareler sınıflandırıcı yazacaksınız. Son olarak, menzil içindeki WiFi ağlarını temel alarak bulunduğunuz odayı tahmin etmek için en küçük kareler modelini kullanacağız.

Adım 1: İlk Masaüstü Uygulaması

Bu adımda Electron JS kullanarak yeni bir masaüstü uygulaması oluşturacağız. Başlamak için, bunun yerine Node paket yöneticisi npm ve wget indirme yardımcı programını kullanacağız.

 brew install npm wget

Başlamak için yeni bir Node projesi oluşturacağız.

 npm init

Bu, sizden paket adını ve ardından sürüm numarasını ister. riot varsayılan adını ve 1.0.0 varsayılan sürümünü kabul etmek için ENTER basın.

 package name: (riot) version: (1.0.0)

Bu, sizden bir proje açıklaması ister. İstediğiniz boş olmayan açıklamayı ekleyin. Aşağıda, açıklama room detector

 description: room detector

Bu, sizden giriş noktasını veya projeyi çalıştıracağınız ana dosyayı ister. app.js girin.

 entry point: (index.js) app.js

Bu, sizden test command ve git repository ister. Şimdilik bu alanları atlamak için ENTER basın.

 test command: git repository:

Bu, sizden keywords ve author ister. İstediğiniz değerleri girin. Aşağıda, anahtar kelimeler için iot , wifi ve yazar için John Doe kullanıyoruz.

 keywords: iot,wifi author: John Doe

Bu, sizden lisans ister. ISC varsayılan değerini kabul etmek için ENTER basın.

 license: (ISC)

Bu noktada, npm size şimdiye kadarki bilgilerin bir özetini soracaktır. Çıktınız aşağıdakine benzer olmalıdır.

 { "name": "riot", "version": "1.0.0", "description": "room detector", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "iot", "wifi" ], "author": "John Doe", "license": "ISC" }

Kabul etmek için ENTER basın. npm daha sonra bir package.json üretir. Tekrar kontrol edilecek tüm dosyaları listeleyin.

 ls

Bu, sanal ortam klasörüyle birlikte bu dizindeki tek dosyayı çıkaracaktır.

 package.json riot

Projemiz için NodeJS bağımlılıklarını kurun.

 npm install electron --global # makes electron binary accessible globally npm install node-wifi --save

Aşağıdakileri kullanarak dosyayı indirerek Electron Quick main.js ile başlayın. Aşağıdaki -O bağımsız değişkeni, app.js main.js olarak yeniden adlandırır.

 wget https://raw.githubusercontent.com/electron/electron-quick-start/master/main.js -O app.js

app.js nano veya favori metin düzenleyicinizde açın.

 nano app.js

12. satırda index.html'yi static/index.html olarak değiştirin, çünkü tüm HTML şablonlarını içeren static bir dizin oluşturacağız.

 function createWindow () { // Create the browser window. win = new BrowserWindow({width: 1200, height: 800}) // and load the index.html of the app. win.loadFile('static/index.html') // Open the DevTools.

Değişikliklerinizi kaydedin ve düzenleyiciden çıkın. Dosyanız, app.js dosyasının kaynak koduyla eşleşmelidir. Şimdi HTML şablonlarımızı barındıracak yeni bir dizin oluşturun.

 mkdir static

Bu proje için oluşturulmuş bir stil sayfasını indirin.

 wget https://raw.githubusercontent.com/alvinwan/riot/master/static/style.css?token=AB-ObfDtD46ANlqrObDanckTQJ2Q1Pyuks5bf79PwA%3D%3D -O static/style.css

static/index.html nano veya favori metin düzenleyicinizde açın. Standart HTML yapısıyla başlayın.

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Riot | Room Detector</title> </head> <body> <main> </main> </body> </html>

Başlığın hemen ardından, Google Fonts ve stil sayfası ile bağlantılı Montserrat yazı tipini bağlayın.

 <title>Riot | Room Detector</title> <!-- start new code --> <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet"> <link href="style.css" rel="stylesheet"> <!-- end new code --> </head>

main etiketler arasına tahmini oda adı için bir yuva ekleyin.

 <main> <!-- start new code --> <p class="text">I believe you're in the</p> <h1 class="title">(I dunno)</h1> <!-- end new code --> </main>

Komut dosyanız şimdi aşağıdakiyle tam olarak eşleşmelidir. Düzenleyiciden çıkın.

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Riot | Room Detector</title> <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet"> <link href="style.css" rel="stylesheet"> </head> <body> <main> <p class="text">I believe you're in the</p> <h1 class="title">(I dunno)</h1> </main> </body> </html>

Şimdi, paket dosyasını bir başlatma komutu içerecek şekilde değiştirin.

 nano package.json

7. satırdan hemen sonra, electron . olarak adlandırılan bir start komutu ekleyin. . Önceki satırın sonuna virgül eklediğinizden emin olun.

 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "electron ." },

Kaydet ve çık. Artık masaüstü uygulamanızı Electron JS'de başlatmaya hazırsınız. Uygulamanızı başlatmak için npm kullanın.

 npm start

Masaüstü uygulamanız aşağıdakilerle eşleşmelidir.

düğmeli ana sayfa
"Yeni Oda Ekle" düğmesi bulunan ana sayfa (Büyük önizleme)

Bu, başlangıç ​​masaüstü uygulamanızı tamamlar. Çıkmak için terminalinize ve CTRL+C'ye geri dönün. Bir sonraki adımda, wifi ağlarını kaydedeceğiz ve kayıt yardımcı programını masaüstü uygulaması kullanıcı arayüzü üzerinden erişilebilir hale getireceğiz.

2. Adım: WiFi Ağlarını Kaydedin

Bu adımda, tüm menzil içi wifi ağlarının gücünü ve sıklığını kaydeden bir NodeJS betiği yazacaksınız. Komut dosyalarınız için bir dizin oluşturun.

 mkdir scripts

scripts/observe.js nano veya favori metin düzenleyicinizde açın.

 nano scripts/observe.js

Bir NodeJS wifi yardımcı programını ve dosya sistemi nesnesini içe aktarın.

 var wifi = require('node-wifi'); var fs = require('fs');

Bir tamamlama işleyicisini kabul eden bir record işlevi tanımlayın.

 /** * Uses a recursive function for repeated scans, since scans are asynchronous. */ function record(n, completion, hook) { }

Yeni işlevin içinde, wifi yardımcı programını başlatın. Bu değer şu anda alakasız olduğundan, rastgele bir wifi arabirimini başlatmak için iface null olarak ayarlayın.

 function record(n, completion, hook) { wifi.init({ iface : null }); }

Örneklerinizi içerecek bir dizi tanımlayın. Örnekler , modelimiz için kullanacağımız eğitim verileridir. Bu özel eğitimdeki örnekler, menzil içi wifi ağlarının ve bunların ilgili güçleri, frekansları, adları vb. listeleridir.

 function record(n, completion, hook) { ... samples = [] }

Wi-Fi taramalarını eşzamansız olarak başlatacak bir özyinelemeli startScan işlevi tanımlayın. Tamamlandıktan sonra, asenkron wifi taraması tekrar tekrar startScan çağırır.

 function record(n, completion, hook) { ... function startScan(i) { wifi.scan(function(err, networks) { }); } startScan(n); }

wifi.scan geri aramasında, hataları veya boş ağ listelerini kontrol edin ve varsa taramayı yeniden başlatın.

 wifi.scan(function(err, networks) { if (err || networks.length == 0) { startScan(i); return } });

Tamamlama işleyicisini çağıran özyinelemeli işlevin temel durumunu ekleyin.

 wifi.scan(function(err, networks) { ... if (i <= 0) { return completion({samples: samples}); } });

Bir ilerleme güncellemesi çıktısı alın, örnekler listesine ekleyin ve özyinelemeli çağrıyı yapın.

 wifi.scan(function(err, networks) { ... hook(n-i+1, networks); samples.push(networks); startScan(i-1); });

Dosyanızın sonunda, örnekleri diskteki bir dosyaya kaydeden bir geri arama ile record işlevini çağırın.

 function record(completion) { ... } function cli() { record(1, function(data) { fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() {}); }, function(i, networks) { console.log(" * [INFO] Collected sample " + (21-i) + " with " + networks.length + " networks"); }) } cli();

Dosyanızın aşağıdakilerle eşleştiğini iki kez kontrol edin:

 var wifi = require('node-wifi'); var fs = require('fs'); /** * Uses a recursive function for repeated scans, since scans are asynchronous. */ function record(n, completion, hook) { wifi.init({ iface : null // network interface, choose a random wifi interface if set to null }); samples = [] function startScan(i) { wifi.scan(function(err, networks) { if (err || networks.length == 0) { startScan(i); return } if (i <= 0) { return completion({samples: samples}); } hook(n-i+1, networks); samples.push(networks); startScan(i-1); }); } startScan(n); } function cli() { record(1, function(data) { fs.writeFile('samples.json', JSON.stringify(data), 'utf8', function() {}); }, function(i, networks) { console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks"); }) } cli();

Kaydet ve çık. Komut dosyasını çalıştırın.

 node scripts/observe.js

Çıktınız, değişken sayıda ağ ile aşağıdakilerle eşleşecektir.

 * [INFO] Collected sample 1 with 39 networks

Yeni toplanan örnekleri inceleyin. json_pp güzel bir şekilde yazdırmak için json_pp'ye ve ilk 16 satırı görüntülemek için başa boruya yönlendirin.

 cat samples.json | json_pp | head -16

Aşağıda 2,4 GHz ağ için örnek çıktı verilmiştir.

 { "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)" ] },

Bu, NodeJS kablosuz tarama komut dosyanızı tamamlar. Bu, tüm menzil içi WiFi ağlarını görüntülememizi sağlar. Bir sonraki adımda, bu komut dosyasını masaüstü uygulamasından erişilebilir hale getireceksiniz.

3. Adım: Tarama Komut Dosyasını Masaüstü Uygulamasına Bağlayın

Bu adımda, komut dosyasını tetiklemek için önce masaüstü uygulamasına bir düğme ekleyeceksiniz. Ardından, komut dosyasının ilerleyişi ile masaüstü uygulaması kullanıcı arayüzünü güncelleyeceksiniz.

static/index.html açın.

 nano static/index.html

Aşağıda gösterildiği gibi “Ekle” düğmesini yerleştirin.

 <h1 class="title">(I dunno)</h1> <!-- start new code --> <div class="buttons"> <a href="add.html" class="button">Add new room</a> </div> <!-- end new code --> </main>

Kaydet ve çık. static/add.html açın.

 nano static/add.html

Aşağıdaki içeriği yapıştırın.

 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Riot | Add New Room</title> <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet"> <link href="style.css" rel="stylesheet"> </head> <body> <main> <h1 class="title">0</h1> <p class="subtitle">of <span>20</span> samples needed. Feel free to move around the room.</p> <input type="text" class="text-field" placeholder="(room name)"> <div class="buttons"> <a href="#" class="button">Start recording</a> <a href="index.html" class="button light">Cancel</a> </div> <p class="text"></p> </main> <script> require('../scripts/observe.js') </script> </body> </html>

Kaydet ve çık. scripts/observe.js açın.

 nano scripts/observe.js

cli işlevinin altında yeni bir ui işlevi tanımlayın.

 function cli() { ... } // start new code function ui() { } // end new code cli();

İşlevin çalışmaya başladığını belirtmek için masaüstü uygulaması durumunu güncelleyin.

 function ui() { var room_name = document.querySelector('#add-room-name').value; var status = document.querySelector('#add-status'); var number = document.querySelector('#add-title'); status.style.display = "block" status.innerHTML = "Listening for wifi..." }

Verileri eğitim ve doğrulama veri kümelerine ayırın.

 function ui() { ... function completion(data) { 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); } }

Yine de completion geri araması dahilinde, her iki veri kümesini de diske yazın.

 function ui() { ... function completion(data) { ... 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] Done") status.innerHTML = "Done." } }

20 örneği kaydetmek ve örnekleri diske record için uygun geri aramalarla kaydı çağırın.

 function ui() { ... function completion(data) { ... } record(20, completion, function(i, networks) { number.innerHTML = i console.log(" * [INFO] Collected sample " + i + " with " + networks.length + " networks") }) }

Son olarak, uygun olduğunda cli ve ui işlevlerini çağırın. cli(); dosyanın altındaki arayın.

 function ui() { ... } cli(); // remove me

Belge nesnesinin küresel olarak erişilebilir olup olmadığını kontrol edin. Değilse, komut dosyası komut satırından çalıştırılıyor. Bu durumda, cli işlevini çağırın. Öyleyse, komut dosyası masaüstü uygulamasından yüklenir. Bu durumda, tıklama dinleyicisini kullanıcı ui işlevine bağlayın.

 if (typeof document == 'undefined') { cli(); } else { document.querySelector('#start-recording').addEventListener('click', ui) }

Kaydet ve çık. Verilerimizi tutmak için bir dizin oluşturun.

 mkdir data

Masaüstü uygulamasını başlatın.

 npm start

Aşağıdaki ana sayfayı göreceksiniz. "Oda ekle"ye tıklayın.

(Büyük önizleme)

Aşağıdaki formu göreceksiniz. Oda için bir ad yazın. Daha sonra kullanacağımız için bu ismi hatırlayın. Örneğimiz bedroom olacak.

Yeni Oda sayfası ekle
"Yeni Oda Ekle" sayfası yükleniyor (Büyük önizleme)

“Kaydı başlat” seçeneğine tıkladığınızda, “Wifi için dinleniyor…” durumunu göreceksiniz.

kayda başlama
“Yeni Oda Ekle” kaydı başlatma (Büyük Önizleme)

20 örneğin tümü kaydedildiğinde, uygulamanız aşağıdakilerle eşleşir. Durum "Bitti" yazacaktır.

Kayıt tamamlandıktan sonra “Yeni Oda Ekle” sayfası (Büyük önizleme)

Aşağıdakilerle eşleşen ana sayfaya dönmek için yanlış adlandırılan “İptal”e tıklayın.

bitmiş kayıt
Kayıt tamamlandıktan sonra “Yeni Oda Ekle” sayfası (Büyük önizleme)

Artık tüm kayıtlı örnekleri diskteki dosyalara kaydedecek olan masaüstü kullanıcı arayüzünden wifi ağlarını tarayabiliriz. Ardından, topladığınız veriler üzerinde kullanıma hazır bir makine öğrenme algoritması-en küçük kareler eğiteceğiz.

Adım 4: Python Eğitim Komut Dosyası Yazın

Bu adımda Python'da bir eğitim scripti yazacağız. Eğitim yardımcı programlarınız için bir dizin oluşturun.

 mkdir model

model/train.py açın

 nano model/train.py

Dosyanızın en üstünde, en küçük kareler modeli için numpy hesaplama kitaplığını ve scipy içe aktarın.

 import numpy as np from scipy.linalg import lstsq import json import sys

Sonraki üç yardımcı program, diskteki dosyalardan veri yükleme ve ayarlama işlemlerini gerçekleştirecektir. İç içe listeleri düzleştiren bir yardımcı program işlevi ekleyerek başlayın. Bunu, bir numune listesini düzleştirmek için kullanacaksınız.

 import sys def flatten(list_of_lists): """Flatten a list of lists to make a list. >>> flatten([[1], [2], [3, 4]]) [1, 2, 3, 4] """ return sum(list_of_lists, [])

Belirtilen dosyalardan örnekleri yükleyen ikinci bir yardımcı program ekleyin. Bu yöntem, örneklerin birden çok dosyaya yayıldığı gerçeğini ortadan kaldırır ve tüm örnekler için yalnızca tek bir oluşturucu döndürür. Numunelerin her biri için etiket, dosyanın indeksidir. örneğin, get_all_samples('a.json', 'b.json') a.json tüm örneklerin etiketi 0 ve b.json tüm örneklerin etiketi 1 olacaktır.

 def get_all_samples(paths): """Load all samples from JSON files.""" for label, path in enumerate(paths): with open(path) as f: for sample in json.load(f)['samples']: signal_levels = [ network['signal_level'].replace('RSSI', '') or 0 for network in sample] yield [network['mac'] for network in sample], signal_levels, label

Ardından, bir kelime torbası benzeri model kullanarak örnekleri kodlayan bir yardımcı program ekleyin. İşte bir örnek: İki örnek topladığımızı varsayalım.

  1. Wi-Fi ağı A gücü 10'da ve Wi-Fi ağı B gücü 15'te
  2. Wi-Fi ağı B gücü 20'de ve Wi-Fi ağı C gücü 25'te.

Bu işlev, örneklerin her biri için üç sayıdan oluşan bir liste üretecektir: ilk değer, wifi ağı A'nın gücü, ikincisi ağ B için ve üçüncüsü C içindir. Gerçekte, biçim [A, B, C şeklindedir. ].

  1. [10, 15, 0]
  2. [0, 20, 25]
 def bag_of_words(all_networks, all_strengths, ordering): """Apply bag-of-words encoding to categorical variables. >>> samples = bag_of_words( ... [['a', 'b'], ['b', 'c'], ['a', 'c']], ... [[1, 2], [2, 3], [1, 3]], ... ['a', 'b', 'c']) >>> next(samples) [1, 2, 0] >>> next(samples) [0, 2, 3] """ for networks, strengths in zip(all_networks, all_strengths): yield [strengths[networks.index(network)] if network in networks else 0 for network in ordering]

Yukarıdaki üç yardımcı programın tümünü kullanarak, bir numune koleksiyonunu ve etiketlerini sentezliyoruz. get_all_samples kullanarak tüm örnekleri ve etiketleri toplayın. Tüm örnekleri one-hot kodlamak için tutarlı bir format tanımlayın, ardından örneklere ordering kodlamasını one_hot . Son olarak, verileri oluşturun ve sırasıyla X ve Y matrislerini etiketleyin.

 def create_dataset(classpaths, ordering=None): """Create dataset from a list of paths to JSON files.""" networks, strengths, labels = zip(*get_all_samples(classpaths)) if ordering is None: ordering = list(sorted(set(flatten(networks)))) X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64) Y = np.array(list(labels)).astype(np.int) return X, Y, ordering

Bu işlevler veri hattını tamamlar. Ardından, model tahminini ve değerlendirmesini soyutlarız. Tahmin yöntemini tanımlayarak başlayın. İlk fonksiyon model çıktılarımızı normalleştirir, böylece tüm değerlerin toplamı 1 olur ve tüm değerler negatif olmaz; bu, çıktının geçerli bir olasılık dağılımı olmasını sağlar. İkincisi modeli değerlendirir.

 def softmax(x): """Convert one-hotted outputs into probability distribution""" x = np.exp(x) return x / np.sum(x) def predict(X, w): """Predict using model parameters""" return np.argmax(softmax(X.dot(w)), axis=1)

Ardından, modelin doğruluğunu değerlendirin. İlk satır, modeli kullanarak tahmini çalıştırır. İkincisi, hem tahmin edilen hem de gerçek değerlerin kaç kez uyuştuğunu sayar ve ardından toplam numune sayısına göre normalleştirir.

 def evaluate(X, Y, w): """Evaluate model w on samples X and labels Y.""" Y_pred = predict(X, w) accuracy = (Y == Y_pred).sum() / X.shape[0] return accuracy

Bu, tahmin ve değerlendirme yardımcı programlarımızı tamamlar. Bu yardımcı programlardan sonra, veri kümesini toplayacak, eğitecek ve değerlendirecek bir main işlev tanımlayın. sys.argv komut satırındaki argümanların listesini okuyarak başlayın; bunlar eğitime dahil edilecek odalardır. Ardından, belirtilen tüm odalardan büyük bir veri kümesi oluşturun.

 def main(): classes = sys.argv[1:] train_paths = sorted(['data/{}_train.json'.format(name) for name in classes]) test_paths = sorted(['data/{}_test.json'.format(name) for name in classes]) X_train, Y_train, ordering = create_dataset(train_paths) X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering)

Etiketlere tek sıcak kodlama uygulayın. Tek-sıcak kodlama , yukarıdaki kelime torbası modeline benzer; kategorik değişkenleri işlemek için bu kodlamayı kullanırız. Diyelim ki 3 olası etiketimiz var. 1, 2 veya 3 olarak etiketlemek yerine verileri [1, 0, 0], [0, 1, 0] veya [0, 0, 1] ile etiketliyoruz. Bu öğretici için, tek-sıcak kodlamanın neden önemli olduğuna ilişkin açıklamayı ayıracağız. Modeli eğitin ve hem tren hem de doğrulama kümelerinde değerlendirin.

 def main(): ... X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering) Y_train_oh = np.eye(len(classes))[Y_train] w, _, _, _ = lstsq(X_train, Y_train_oh) train_accuracy = evaluate(X_train, Y_train, w) test_accuracy = evaluate(X_test, Y_test, w)

Her iki doğruluğu da yazdırın ve modeli diske kaydedin.

 def main(): ... print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, test_accuracy*100)) np.save('w.npy', w) np.save('ordering.npy', np.array(ordering)) sys.stdout.flush()

Dosyanın sonunda main işlevi çalıştırın.

 if __name__ == '__main__': main()

Kaydet ve çık. Dosyanızın aşağıdakilerle eşleştiğini iki kez kontrol edin:

 import numpy as np from scipy.linalg import lstsq import json import sys def flatten(list_of_lists): """Flatten a list of lists to make a list. >>> flatten([[1], [2], [3, 4]]) [1, 2, 3, 4] """ return sum(list_of_lists, []) def get_all_samples(paths): """Load all samples from JSON files.""" for label, path in enumerate(paths): with open(path) as f: for sample in json.load(f)['samples']: signal_levels = [ network['signal_level'].replace('RSSI', '') or 0 for network in sample] yield [network['mac'] for network in sample], signal_levels, label def bag_of_words(all_networks, all_strengths, ordering): """Apply bag-of-words encoding to categorical variables. >>> samples = bag_of_words( ... [['a', 'b'], ['b', 'c'], ['a', 'c']], ... [[1, 2], [2, 3], [1, 3]], ... ['a', 'b', 'c']) >>> next(samples) [1, 2, 0] >>> next(samples) [0, 2, 3] """ for networks, strengths in zip(all_networks, all_strengths): yield [int(strengths[networks.index(network)]) if network in networks else 0 for network in ordering] def create_dataset(classpaths, ordering=None): """Create dataset from a list of paths to JSON files.""" networks, strengths, labels = zip(*get_all_samples(classpaths)) if ordering is None: ordering = list(sorted(set(flatten(networks)))) X = np.array(list(bag_of_words(networks, strengths, ordering))).astype(np.float64) Y = np.array(list(labels)).astype(np.int) return X, Y, ordering def softmax(x): """Convert one-hotted outputs into probability distribution""" x = np.exp(x) return x / np.sum(x) def predict(X, w): """Predict using model parameters""" return np.argmax(softmax(X.dot(w)), axis=1) def evaluate(X, Y, w): """Evaluate model w on samples X and labels Y.""" Y_pred = predict(X, w) accuracy = (Y == Y_pred).sum() / X.shape[0] return accuracy def main(): classes = sys.argv[1:] train_paths = sorted(['data/{}_train.json'.format(name) for name in classes]) test_paths = sorted(['data/{}_test.json'.format(name) for name in classes]) X_train, Y_train, ordering = create_dataset(train_paths) X_test, Y_test, _ = create_dataset(test_paths, ordering=ordering) Y_train_oh = np.eye(len(classes))[Y_train] w, _, _, _ = lstsq(X_train, Y_train_oh) train_accuracy = evaluate(X_train, Y_train, w) validation_accuracy = evaluate(X_test, Y_test, w) print('Train accuracy ({}%), Validation accuracy ({}%)'.format(train_accuracy*100, validation_accuracy*100)) np.save('w.npy', w) np.save('ordering.npy', np.array(ordering)) sys.stdout.flush() if __name__ == '__main__': main()

Kaydet ve çık. 20 örneği kaydederken yukarıda kullanılan oda adını hatırlayın. Aşağıdaki bedroom yerine bu adı kullanın. Örneğimiz bedroom . Bir LAPACK hatasından gelen uyarıları yok saymak için -W ignore geliriz.

 python -W ignore model/train.py bedroom

Yalnızca bir oda için eğitim örnekleri topladığımızdan, %100 eğitim ve doğrulama doğruluğu görmeniz gerekir.

 Train accuracy (100.0%), Validation accuracy (100.0%)

Ardından, bu eğitim komut dosyasını masaüstü uygulamasına bağlayacağız.

Adım 5: Tren Komut Dosyasını Bağlayın

Bu adımda, kullanıcı yeni bir grup numune topladığı zaman modeli otomatik olarak yeniden eğiteceğiz. scripts/observe.js açın.

 nano scripts/observe.js

fs içe aktarma işleminden hemen sonra, alt süreç oluşturucuyu ve yardımcı programları içe aktarın.

 var fs = require('fs'); // start new code const spawn = require("child_process").spawn; var utils = require('./utils.js');

ui işlevinde, tamamlama işleyicisinin sonuna retrain için aşağıdaki çağrıyı ekleyin.

 function ui() { ... function completion() { ... retrain((data) => { var status = document.querySelector('#add-status'); accuracies = data.toString().split('\n')[0]; status.innerHTML = "Retraining succeeded: " + accuracies }); } ... }

ui işlevinden sonra aşağıdaki retrain işlevini ekleyin. Bu, python betiğini çalıştıracak bir alt süreç oluşturur. Tamamlandığında, süreç bir tamamlama işleyicisini çağırır. Başarısızlık durumunda, hata mesajını günlüğe kaydeder.

 function ui() { .. } function retrain(completion) { var filenames = utils.get_filenames() const pythonProcess = spawn('python', ["./model/train.py"].concat(filenames)); pythonProcess.stdout.on('data', completion); pythonProcess.stderr.on('data', (data) => { console.log(" * [ERROR] " + data.toString()) }) }

Kaydet ve çık. scripts/utils.js açın.

 nano scripts/utils.js

data/ içindeki tüm veri kümelerini almak için aşağıdaki yardımcı programı ekleyin.

 var fs = require('fs'); module.exports = { get_filenames: get_filenames } function get_filenames() { filenames = new Set([]); fs.readdirSync("data/").forEach(function(filename) { filenames.add(filename.replace('_train', '').replace('_test', '').replace('.json', '' )) }); filenames = Array.from(filenames.values()) filenames.sort(); filenames.splice(filenames.indexOf('.DS_Store'), 1) return filenames }

Kaydet ve çık. Bu adımın sonuçlanması için fiziksel olarak yeni bir yere gidin. İdeal olarak, orijinal konumunuz ile yeni konumunuz arasında bir duvar olmalıdır. Engeller ne kadar fazlaysa, masaüstü uygulamanız o kadar iyi çalışır.

Bir kez daha, masaüstü uygulamanızı çalıştırın.

 npm start

Daha önce olduğu gibi, eğitim komut dosyasını çalıştırın. "Oda ekle"ye tıklayın.

düğmeli ana sayfa
"Yeni Oda Ekle" düğmesi bulunan ana sayfa (Büyük önizleme)

İlk odanızdan farklı bir oda adı yazın. living room kullanacağız.

Yeni Oda sayfası ekle
"Yeni Oda Ekle" sayfası yükleniyor (Büyük önizleme)

“Kaydı başlat” seçeneğine tıkladığınızda, “Wifi için dinleniyor…” durumunu göreceksiniz.

İkinci oda için kaydı başlatan “Yeni Oda Ekle” (Geniş önizleme)

20 örneğin tümü kaydedildiğinde, uygulamanız aşağıdakilerle eşleşir. Durum "Bitti. Yeniden eğitim modeli…”

bitmiş kayıt 2
İkinci oda için kayıt tamamlandıktan sonra "Yeni Oda Ekle" sayfası (Geniş önizleme)

Bir sonraki adımda, içinde bulunduğunuz odayı anında tahmin etmek için bu yeniden eğitilmiş modeli kullanacağız.

Adım 6: Python Değerlendirme Komut Dosyası Yazın

Bu adımda, önceden eğitilmiş model parametrelerini yükleyeceğiz, wifi ağlarını tarayacağız ve taramaya göre odayı tahmin edeceğiz.

model/eval.py açın.

 nano model/eval.py

Son betiğimizde kullanılan ve tanımlanan kitaplıkları içe aktarın.

 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

Tüm veri kümelerinin adlarını çıkarmak için bir yardımcı program tanımlayın. Bu işlev, tüm veri kümelerinin data/ içinde <dataset>_train.json ve <dataset>_test.json olarak depolandığını varsayar.

 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}))

main işlevi tanımlayın ve eğitim komut dosyasından kaydedilen parametreleri yükleyerek başlayın.

 def get_datasets(): ... def main(): w = np.load('w.npy') ordering = np.load('ordering.npy')

Veri kümesini oluşturun ve tahmin edin.

 def main(): ... classpaths = [sys.argv[1]] X, _, _ = create_dataset(classpaths, ordering) y = np.asscalar(predict(X, w))

İlk iki olasılık arasındaki farka dayalı olarak bir güven puanı hesaplayın.

 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)

Son olarak, kategoriyi çıkarın ve sonucu yazdırın. Komut dosyasını sonlandırmak için main işlevi çağırın.

 def main() ... category = get_datasets()[y] print(json.dumps({"category": category, "confidence": confidence})) if __name__ == '__main__': main()

Kaydet ve çık. Kodunuzun aşağıdakiyle eşleştiğini iki kez kontrol edin (kaynak kodu):

 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()

Ardından, bu değerlendirme komut dosyasını masaüstü uygulamasına bağlayacağız. Masaüstü uygulaması, sürekli olarak wifi taramaları çalıştıracak ve kullanıcı arayüzünü tahmini oda ile güncelleyecektir.

7. Adım: Değerlendirmeyi Masaüstü Uygulamasına Bağlayın

Bu adımda, kullanıcı arayüzünü bir “güven” ekranı ile güncelleyeceğiz. Ardından, ilişkili NodeJS betiği sürekli olarak taramaları ve tahminleri çalıştıracak ve kullanıcı arayüzünü buna göre güncelleyecektir.

static/index.html açın.

 nano static/index.html

Başlıktan hemen sonra ve düğmelerden önce güven için bir satır ekleyin.

 <h1 class="title">(I dunno)</h1> <!-- start new code --> <p class="subtitle">with <span>0%</span> confidence</p> <!-- end new code --> <div class="buttons">

main hemen sonra, ancak body sonundan önce, yeni bir komut dosyası predict.js ekleyin.

 </main> <!-- start new code --> <script> require('../scripts/predict.js') </script> <!-- end new code --> </body>

Kaydet ve çık. scripts/predict.js açın.

 nano scripts/predict.js

Dosya sistemi, yardımcı programlar ve alt süreç oluşturucu için gerekli NodeJS yardımcı programlarını içe aktarın.

 var fs = require('fs'); var utils = require('./utils'); const spawn = require("child_process").spawn;

Wifi ağlarını algılamak için ayrı bir düğüm sürecini ve odayı tahmin etmek için ayrı bir Python sürecini çağıran bir predict işlevi tanımlayın.

 function predict(completion) { const nodeProcess = spawn('node', ["scripts/observe.js"]); const pythonProcess = spawn('python', ["-W", "ignore", "./model/eval.py", "samples.json"]); }

Her iki işlem de oluşturulduktan sonra, hem başarımlar hem de hatalar için Python işlemine geri aramalar ekleyin. Başarılı geri arama, bilgileri günlüğe kaydeder, tamamlama geri aramasını başlatır ve kullanıcı arabirimini tahmin ve güvenle günceller. Hata geri araması hatayı günlüğe kaydeder.

 function predict(completion) { ... pythonProcess.stdout.on('data', (data) => { information = JSON.parse(data.toString()); console.log(" * [INFO] Room '" + information.category + "' with confidence '" + information.confidence + "'") completion() if (typeof document != "undefined") { document.querySelector('#predicted-room-name').innerHTML = information.category document.querySelector('#predicted-confidence').innerHTML = information.confidence } }); pythonProcess.stderr.on('data', (data) => { console.log(data.toString()); }) }

predict işlevini sonsuza kadar yinelemeli olarak çağırmak için bir ana işlev tanımlayın.

 function main() { f = function() { predict(f) } predict(f) } main();

Son bir kez, canlı tahmini görmek için masaüstü uygulamasını açın.

 npm start

Yaklaşık her saniye, bir tarama tamamlanacak ve arayüz, en son güven ve tahmini oda ile güncellenecektir. Tebrikler; tüm menzil içi WiFi ağlarına dayalı basit bir oda dedektörü tamamladınız.

demo
Odanın içinde 20 örnek ve koridorda 20 örnek daha kaydediyor. İçeriye geri döndüğünüzde, senaryo doğru bir şekilde "koridor" ve ardından "yatak odası" öngörüyor. (Büyük önizleme)

Çözüm

Bu öğreticide, bir bina içindeki konumunuzu algılamak için yalnızca masaüstünüzü kullanarak bir çözüm oluşturduk. Electron JS kullanarak basit bir masaüstü uygulaması oluşturduk ve tüm menzil içi WiFi ağlarında basit bir makine öğrenimi yöntemi uyguladık. Bu, bakımı maliyetli (para açısından değil, zaman ve geliştirme açısından maliyet) cihaz dizilerine ihtiyaç duymadan Nesnelerin İnterneti uygulamalarının önünü açar.

Not : Kaynak kodunun tamamını Github üzerinde görebilirsiniz.

Zamanla, bu en küçük karelerin aslında olağanüstü bir performans göstermediğini görebilirsiniz. Tek bir oda içinde iki yer bulmayı deneyin veya kapı eşiğinde durun. En küçük kareler, kenar durumları arasında ayrım yapamayacak kadar büyük olacaktır. Daha iyisini yapabilir miyiz? Yapabileceğimiz ortaya çıktı ve gelecekteki derslerde daha iyi performans için diğer tekniklerden ve makine öğreniminin temellerinden yararlanacağız. Bu eğitim, gelecek deneyler için hızlı bir test yatağı görevi görür.