สร้างเครื่องตรวจจับห้องสำหรับอุปกรณ์ IoT บน Mac OS

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างรวดเร็ว ↬ ในบทช่วยสอนนี้ คุณสร้างแอปเดสก์ท็อปที่คาดการณ์ว่าคุณกำลังอยู่ในห้องใดโดยใช้อัลกอริทึมการเรียนรู้ของเครื่องอย่างง่าย: สี่เหลี่ยมน้อยที่สุด โค้ดนี้ใช้กับทุกแพลตฟอร์ม แต่เราจัดเตรียมคำแนะนำในการติดตั้งการพึ่งพาสำหรับ Mac OSX เท่านั้น

การรู้ว่าคุณอยู่ในห้องไหนทำให้แอปพลิเคชัน IoT ต่างๆ ใช้งานได้ ตั้งแต่การเปิดไฟไปจนถึงการเปลี่ยนช่องทีวี แล้วเราจะตรวจจับช่วงเวลาที่คุณและโทรศัพท์ของคุณอยู่ในห้องครัว ห้องนอน หรือห้องนั่งเล่นได้อย่างไร ด้วยฮาร์ดแวร์สำหรับสินค้าโภคภัณฑ์ในปัจจุบัน มีความเป็นไปได้มากมาย:

ทางออกหนึ่งคือการ จัดเตรียมอุปกรณ์บลูทูธให้แต่ละห้อง เมื่อโทรศัพท์ของคุณอยู่ในระยะของอุปกรณ์บลูทูธ โทรศัพท์ของคุณจะรู้ว่าเป็นห้องไหน โดยอิงจากอุปกรณ์บลูทูธ อย่างไรก็ตาม การดูแลรักษาชุดอุปกรณ์บลูทูธถือเป็นค่าใช้จ่ายที่สำคัญ ตั้งแต่การเปลี่ยนแบตเตอรี่ไปจนถึงการเปลี่ยนอุปกรณ์ที่มีปัญหา นอกจากนี้ ความใกล้ชิดกับอุปกรณ์บลูทูธอาจไม่ใช่คำตอบเสมอไป หากคุณอยู่ในห้องนั่งเล่น ข้างผนังที่ใช้ร่วมกับห้องครัว เครื่องใช้ในครัวของคุณไม่ควรเริ่มปั่นอาหาร

อีกวิธีหนึ่งแม้ว่าจะใช้งานไม่ได้ แต่วิธีแก้ไขคือการ ใช้ GPS อย่างไรก็ตาม โปรดทราบว่าหมวก GPS ทำงานได้ไม่ดีในที่ร่ม ซึ่งกำแพงจำนวนมาก สัญญาณอื่นๆ และสิ่งกีดขวางอื่นๆ จะสร้างความเสียหายให้กับความแม่นยำของ GPS

แนวทางของเราคือ ใช้ประโยชน์จากเครือข่าย WiFi ที่อยู่ในระยะทั้งหมด — แม้กระทั่งเครือข่ายที่โทรศัพท์ของคุณไม่ได้เชื่อมต่อด้วย นี่คือวิธี: พิจารณาความแรงของ WiFi A ในห้องครัว สมมติว่าเป็น 5 เนื่องจากมีผนังกั้นระหว่างห้องครัวและห้องนอน เราจึงคาดว่าความแรงของ WiFi A ในห้องนอนจะแตกต่างกัน บอกว่าเป็น 2 เราสามารถใช้ประโยชน์จากความแตกต่างนี้เพื่อคาดการณ์ว่าเราอยู่ในห้องใด มีอะไรเพิ่มเติม: เครือข่าย WiFi B จากเพื่อนบ้านของเราสามารถตรวจพบได้จากห้องนั่งเล่นเท่านั้น แต่จะมองไม่เห็นจากห้องครัวอย่างมีประสิทธิภาพ ที่ทำให้การทำนายง่ายยิ่งขึ้น โดยสรุป รายการ WiFi ที่อยู่ในระยะทั้งหมดให้ข้อมูลมากมายแก่เรา

วิธีนี้มีข้อดีที่ชัดเจนของ:

  1. ไม่ต้องการฮาร์ดแวร์เพิ่มเติม
  2. อาศัยสัญญาณที่เสถียรกว่าเช่น WiFi
  3. ทำงานได้ดีเมื่อเทคนิคอื่นๆ เช่น GPS อ่อนแอ

ยิ่งมีกำแพงมากเท่าไร ยิ่งมีจุดแข็งของเครือข่าย WiFi แตกต่างกันมากเท่าไร ก็ยิ่งจำแนกห้องได้ง่ายขึ้นเท่านั้น คุณจะสร้างแอปเดสก์ท็อปที่เรียบง่ายซึ่งรวบรวมข้อมูล เรียนรู้จากข้อมูล และคาดการณ์ว่าคุณอยู่ในห้องใดในเวลาใดก็ตาม

อ่านเพิ่มเติม เกี่ยวกับ SmashingMag:

  • การเพิ่มขึ้นของ UI การสนทนาอัจฉริยะ
  • การประยุกต์ใช้แมชชีนเลิร์นนิงสำหรับนักออกแบบ
  • วิธีการสร้างต้นแบบประสบการณ์ IoT: การสร้างฮาร์ดแวร์
  • การออกแบบสำหรับอินเทอร์เน็ตของสิ่งต่าง ๆ ทางอารมณ์

ข้อกำหนดเบื้องต้น

สำหรับบทช่วยสอนนี้ คุณจะต้องมี Mac OSX ในขณะที่รหัสสามารถใช้ได้กับทุกแพลตฟอร์ม เราจะจัดเตรียมคำแนะนำในการติดตั้งการพึ่งพาสำหรับ Mac เท่านั้น

  • Mac OSX
  • Homebrew ตัวจัดการแพ็คเกจสำหรับ Mac OSX ในการติดตั้ง ให้คัดลอกและวางคำสั่งที่ brew.sh
  • การติดตั้ง NodeJS 10.8.0+ และ npm
  • การติดตั้ง Python 3.6+ และ pip ดู 3 ส่วนแรกของ “วิธีการติดตั้ง virtualenv การติดตั้งด้วย pip และการจัดการแพ็คเกจ”
เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ขั้นตอนที่ 0: ตั้งค่าสภาพแวดล้อมการทำงาน

แอพเดสก์ท็อปของคุณจะถูกเขียนใน NodeJS อย่างไรก็ตาม เพื่อใช้ประโยชน์จากไลบรารีการคำนวณที่มีประสิทธิภาพมากขึ้น เช่น numpy โค้ดการฝึกอบรมและการทำนายจะถูกเขียนใน Python ในการเริ่มต้น เราจะตั้งค่าสภาพแวดล้อมของคุณและติดตั้งการพึ่งพา สร้างไดเร็กทอรีใหม่เพื่อใช้เป็นที่เก็บโครงการของคุณ

 mkdir ~/riot

นำทางไปยังไดเร็กทอรี

 cd ~/riot

ใช้ pip เพื่อติดตั้งตัวจัดการสภาพแวดล้อมเสมือนเริ่มต้นของ Python

 sudo pip install virtualenv

สร้างสภาพแวดล้อมเสมือน Python3.6 ชื่อ riot

 virtualenv riot --python=python3.6

เปิดใช้งานสภาพแวดล้อมเสมือน

 source riot/bin/activate

ข้อความแจ้งของคุณนำหน้าด้วย (riot) นี่แสดงว่าเราเข้าสู่สภาพแวดล้อมเสมือนสำเร็จแล้ว ติดตั้งแพ็คเกจต่อไปนี้โดยใช้ pip :

  • numpy : ห้องสมุดพีชคณิตเชิงเส้นที่มีประสิทธิภาพ
  • scipy : ห้องสมุดคอมพิวเตอร์ทางวิทยาศาสตร์ที่ใช้โมเดลการเรียนรู้ของเครื่องยอดนิยม
 pip install numpy==1.14.3 scipy ==1.1.0

ด้วยการตั้งค่าไดเรกทอรีการทำงาน เราจะเริ่มต้นด้วยแอปเดสก์ท็อปที่บันทึกเครือข่าย WiFi ทั้งหมดในระยะ การบันทึกเหล่านี้จะประกอบเป็นข้อมูลการฝึกสำหรับโมเดลแมชชีนเลิร์นนิงของคุณ เมื่อเรามีข้อมูลในมือแล้ว คุณจะเขียนตัวแยกประเภทกำลังสองน้อยที่สุด ซึ่งฝึกเกี่ยวกับสัญญาณ WiFi ที่รวบรวมไว้ก่อนหน้านี้ สุดท้าย เราจะใช้โมเดลสี่เหลี่ยมจัตุรัสน้อยที่สุดในการคาดคะเนห้องที่คุณอยู่ โดยพิจารณาจากเครือข่าย WiFi ในระยะ

ขั้นตอนที่ 1: แอปพลิเคชันเดสก์ท็อปเริ่มต้น

ในขั้นตอนนี้ เราจะสร้างแอปพลิเคชันเดสก์ท็อปใหม่โดยใช้ Electron JS ในการเริ่มต้น เราจะใช้ตัวจัดการแพ็คเกจโหนด npm และยูทิลิตี้ดาวน์โหลด wget แทน

 brew install npm wget

ในการเริ่มต้น เราจะสร้างโปรเจ็กต์โหนดใหม่

 npm init

ซึ่งจะแจ้งให้คุณใส่ชื่อแพ็กเกจและหมายเลขเวอร์ชัน กด ENTER เพื่อยอมรับชื่อเริ่มต้นของ riot และรุ่นเริ่มต้น 1.0.0

 package name: (riot) version: (1.0.0)

ซึ่งจะแจ้งให้คุณทราบถึงคำอธิบายโครงการ เพิ่มคำอธิบายที่ไม่ว่างเปล่าที่คุณต้องการ ด้านล่างคำอธิบายคือ room detector

 description: room detector

ซึ่งจะแจ้งให้คุณทราบถึงจุดเริ่มต้นหรือไฟล์หลักที่จะเรียกใช้โครงการ ป้อน app.js

 entry point: (index.js) app.js

ซึ่งจะแจ้งให้คุณทราบ test command และ git repository กด ENTER เพื่อข้ามช่องเหล่านี้ไปก่อน

 test command: git repository:

นี้พร้อมท์คุณสำหรับ keywords และ author กรอกค่าใด ๆ ที่คุณต้องการ ด้านล่างนี้ เราใช้ iot , wifi สำหรับคำหลัก และใช้ John Doe สำหรับผู้แต่ง

 keywords: iot,wifi author: John Doe

สิ่งนี้จะแจ้งให้คุณขอใบอนุญาต กด ENTER เพื่อยอมรับค่าเริ่มต้นของ ISC

 license: (ISC)

ณ จุดนี้ npm จะแจ้งสรุปข้อมูลให้คุณทราบ ผลลัพธ์ของคุณควรคล้ายกับต่อไปนี้

 { "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" }

กด ENTER เพื่อยอมรับ npm จะสร้าง package.json แสดงรายการไฟล์ทั้งหมดเพื่อตรวจสอบอีกครั้ง

 ls

สิ่งนี้จะส่งออกไฟล์เดียวในไดเร็กทอรีนี้ พร้อมกับโฟลเดอร์สภาพแวดล้อมเสมือน

 package.json riot

ติดตั้งการพึ่งพา NodeJS สำหรับโครงการของเรา

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

เริ่มต้นด้วย main.js จาก Electron Quick Start โดยการดาวน์โหลดไฟล์โดยใช้ด้านล่าง อาร์กิวเมนต์ -O ต่อไปนี้เปลี่ยนชื่อ main.js เป็น app.js

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

เปิด app.js ใน nano หรือโปรแกรมแก้ไขข้อความที่คุณชื่นชอบ

 nano app.js

ในบรรทัดที่ 12 เปลี่ยน index.html เป็น static/index.html เนื่องจากเราจะสร้างไดเร็กทอรีส static เพื่อให้มีเทมเพลต HTML ทั้งหมด

 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.

บันทึกการเปลี่ยนแปลงของคุณและออกจากตัวแก้ไข ไฟล์ของคุณควรตรงกับซอร์สโค้ดของไฟล์ app.js ตอนนี้สร้างไดเร็กทอรีใหม่เพื่อจัดเก็บเทมเพลต HTML ของเรา

 mkdir static

ดาวน์โหลดสไตล์ชีตที่สร้างขึ้นสำหรับโครงการนี้

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

เปิด static/index.html ใน nano หรือโปรแกรมแก้ไขข้อความที่คุณชื่นชอบ เริ่มต้นด้วยโครงสร้าง HTML มาตรฐาน

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

ต่อจากชื่อเรื่อง ให้ลิงก์แบบอักษรมอนต์เซอร์รัตที่ลิงก์โดย Google แบบอักษรและสไตล์ชีต

 <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 ให้เพิ่มช่องสำหรับชื่อห้องที่คาดคะเน

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

สคริปต์ของคุณควรตรงกับสิ่งต่อไปนี้ทุกประการ ออกจากตัวแก้ไข

 <!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>

ตอนนี้ แก้ไขไฟล์แพ็คเกจให้มีคำสั่ง start

 nano package.json

หลังจากบรรทัดที่ 7 ให้เพิ่มคำสั่ง start ที่ใช้นามแฝงว่า electron . . อย่าลืมใส่เครื่องหมายจุลภาคต่อท้ายบรรทัดก่อนหน้า

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

บันทึกและออก. ตอนนี้คุณพร้อมที่จะเปิดแอปเดสก์ท็อปของคุณใน Electron JS แล้ว ใช้ npm เพื่อเปิดแอปพลิเคชันของคุณ

 npm start

แอปพลิเคชันเดสก์ท็อปของคุณควรตรงกับรายการต่อไปนี้

หน้าแรกด้วยปุ่ม
หน้าแรกที่มีปุ่ม "เพิ่มห้องใหม่" (ตัวอย่างขนาดใหญ่)

การดำเนินการนี้จะทำให้แอปเดสก์ท็อปเริ่มต้นของคุณสมบูรณ์ หากต้องการออก ให้กลับไปที่เทอร์มินัลของคุณและกด CTRL+C ในขั้นตอนต่อไป เราจะบันทึกเครือข่าย wifi และทำให้ยูทิลิตี้การบันทึกสามารถเข้าถึงได้ผ่าน UI ของแอปพลิเคชันเดสก์ท็อป

ขั้นตอนที่ 2: บันทึกเครือข่าย WiFi

ในขั้นตอนนี้ คุณจะเขียนสคริปต์ NodeJS ที่บันทึกความแรงและความถี่ของเครือข่าย wifi ในระยะทั้งหมด สร้างไดเร็กทอรีสำหรับสคริปต์ของคุณ

 mkdir scripts

เปิด scripts/observe.js ใน nano หรือโปรแกรมแก้ไขข้อความที่คุณชื่นชอบ

 nano scripts/observe.js

นำเข้ายูทิลิตี้ NodeJS wifi และอ็อบเจ็กต์ระบบไฟล์

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

กำหนดฟังก์ชัน record ที่ยอมรับตัวจัดการความสมบูรณ์

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

ภายในฟังก์ชันใหม่ ให้เริ่มต้นยูทิลิตี้ wifi ตั้งค่า iface เป็น null เพื่อเริ่มต้นอินเทอร์เฟซ wifi แบบสุ่ม เนื่องจากค่านี้ไม่เกี่ยวข้องในขณะนี้

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

กำหนดอาร์เรย์เพื่อเก็บตัวอย่างของคุณ ตัวอย่าง คือข้อมูลการฝึกอบรมที่เราจะใช้สำหรับแบบจำลองของเรา ตัวอย่างในบทช่วยสอนนี้คือรายการเครือข่าย wifi ในระยะและจุดแข็ง ความถี่ ชื่อ ฯลฯ ที่เกี่ยวข้อง

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

กำหนดฟังก์ชันเรียกซ้ำ startScan ซึ่งจะเริ่มต้นการสแกน wifi แบบอะซิงโครนัส เมื่อเสร็จสิ้น การสแกน wifi แบบอะซิงโครนัสจะเรียกใช้ startScan แบบเรียกซ้ำ

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

ในการเรียกกลับ wifi.scan ให้ตรวจหาข้อผิดพลาดหรือรายการเครือข่ายว่าง แล้วเริ่มการสแกนใหม่ หากมี

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

เพิ่มตัวพิมพ์พื้นฐานของฟังก์ชันแบบเรียกซ้ำ ซึ่งเรียกใช้ตัวจัดการความสมบูรณ์

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

ส่งออกการอัปเดตความคืบหน้า ต่อท้ายรายการตัวอย่าง และโทรซ้ำ

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

ที่ส่วนท้ายของไฟล์ ให้เรียกใช้ฟังก์ชัน record ด้วยการโทรกลับที่บันทึกตัวอย่างลงในไฟล์บนดิสก์

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

ตรวจสอบอีกครั้งว่าไฟล์ของคุณตรงกับรายการต่อไปนี้:

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

บันทึกและออก. เรียกใช้สคริปต์

 node scripts/observe.js

ผลลัพธ์ของคุณจะตรงกับสิ่งต่อไปนี้ พร้อมจำนวนตัวแปรของเครือข่าย

 * [INFO] Collected sample 1 with 39 networks

ตรวจสอบตัวอย่างที่เพิ่งรวบรวม ไปป์ไปที่ json_pp เพื่อพิมพ์ JSON และไพพ์ไปที่หัวเพื่อดู 16 บรรทัดแรก

 cat samples.json | json_pp | head -16

ด้านล่างนี้เป็นตัวอย่างเอาต์พุตสำหรับเครือข่าย 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)" ] },

นี่เป็นการสรุปสคริปต์การสแกน NodeJS wifi ของคุณ ซึ่งช่วยให้เราดูเครือข่าย WiFi ในระยะทั้งหมดได้ ในขั้นตอนต่อไป คุณจะต้องทำให้สคริปต์นี้สามารถเข้าถึงได้จากแอปเดสก์ท็อป

ขั้นตอนที่ 3: เชื่อมต่อสคริปต์สแกนกับแอปเดสก์ท็อป

ในขั้นตอนนี้ คุณจะต้องเพิ่มปุ่มลงในแอปเดสก์ท็อปก่อนเพื่อเรียกใช้สคริปต์ จากนั้น คุณจะอัปเดต UI ของแอปเดสก์ท็อปด้วยความคืบหน้าของสคริปต์

เปิด static/index.html

 nano static/index.html

ใส่ปุ่ม "เพิ่ม" ดังที่แสดงด้านล่าง

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

บันทึกและออก. เปิด static/add.html

 nano static/add.html

วางเนื้อหาต่อไปนี้

 <!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>

บันทึกและออก. เปิด scripts/observe.js อีกครั้ง

 nano scripts/observe.js

ใต้ฟังก์ชัน cli ให้กำหนดฟังก์ชัน ui ใหม่

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

อัปเดตสถานะแอปเดสก์ท็อปเพื่อระบุว่าฟังก์ชันเริ่มทำงานแล้ว

 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..." }

แบ่งข้อมูลออกเป็นชุดข้อมูลการฝึกอบรมและการตรวจสอบความถูกต้อง

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

ยังอยู่ในการเรียกกลับที่ completion ให้เขียนชุดข้อมูลทั้งสองลงในดิสก์

 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." } }

เรียกใช้ record ด้วยการโทรกลับที่เหมาะสมเพื่อบันทึก 20 ตัวอย่างและบันทึกตัวอย่างลงในดิสก์

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

สุดท้าย เรียกใช้ฟังก์ชัน cli และ ui ตามความเหมาะสม เริ่มต้นด้วยการลบ cli(); โทรที่ด้านล่างของไฟล์

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

ตรวจสอบว่าวัตถุเอกสารสามารถเข้าถึงได้ทั่วโลกหรือไม่ ถ้าไม่ แสดงว่าสคริปต์กำลังถูกเรียกใช้จากบรรทัดคำสั่ง ในกรณีนี้ เรียกใช้ฟังก์ชัน cli หากใช่ สคริปต์จะถูกโหลดจากภายในแอปเดสก์ท็อป ในกรณีนี้ ให้ผูก click listener กับฟังก์ชัน ui

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

บันทึกและออก. สร้างไดเร็กทอรีเพื่อเก็บข้อมูลของเรา

 mkdir data

เปิดแอปเดสก์ท็อป

 npm start

คุณจะเห็นหน้าแรกต่อไปนี้ คลิกที่ "เพิ่มห้อง"

(ตัวอย่างขนาดใหญ่)

คุณจะเห็นแบบฟอร์มต่อไปนี้ พิมพ์ชื่อห้อง จำชื่อนี้ไว้เพราะเราจะใช้ชื่อนี้ในภายหลัง ตัวอย่างของเราคือ bedroom .

เพิ่มหน้าห้องใหม่
โหลดหน้า "เพิ่มห้องใหม่" (ตัวอย่างขนาดใหญ่)

คลิก “เริ่มการบันทึก” และคุณจะเห็นสถานะต่อไปนี้ “กำลังฟัง wifi…”

กำลังเริ่มบันทึก
“เพิ่มห้องใหม่” เริ่มบันทึก (ตัวอย่างขนาดใหญ่)

เมื่อบันทึกตัวอย่างทั้งหมด 20 ตัวอย่าง แอปของคุณจะตรงกับรายการต่อไปนี้ สถานะจะขึ้นว่า "เสร็จสิ้น"

หน้า “เพิ่มห้องใหม่” หลังจากบันทึกเสร็จแล้ว (ตัวอย่างขนาดใหญ่)

คลิกที่ชื่อผิด “ยกเลิก” เพื่อกลับไปที่หน้าแรกซึ่งตรงกับรายการต่อไปนี้

บันทึกเสร็จแล้ว
หน้า “เพิ่มห้องใหม่” หลังจากบันทึกเสร็จแล้ว (ตัวอย่างขนาดใหญ่)

ขณะนี้เราสามารถสแกนเครือข่าย wifi จาก UI เดสก์ท็อป ซึ่งจะบันทึกตัวอย่างที่บันทึกไว้ทั้งหมดไปยังไฟล์บนดิสก์ ต่อไป เราจะฝึกอัลกอริทึมการเรียนรู้ของเครื่องนอกกรอบที่มีกำลังสองน้อยที่สุดกับข้อมูลที่คุณรวบรวม

ขั้นตอนที่ 4: เขียนสคริปต์การฝึกอบรม Python

ในขั้นตอนนี้ เราจะเขียนสคริปต์การฝึกอบรมในภาษาไพทอน สร้างไดเร็กทอรีสำหรับยูทิลิตี้การฝึกอบรมของคุณ

 mkdir model

เปิด model/train.py

 nano model/train.py

ที่ด้านบนสุดของไฟล์ของคุณ นำเข้าไลบรารีการคำนวณ numpy และ scipy สำหรับโมเดลสี่เหลี่ยมที่น้อยที่สุด

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

ยูทิลิตี้สามตัวถัดไปจะจัดการการโหลดและตั้งค่าข้อมูลจากไฟล์บนดิสก์ เริ่มต้นด้วยการเพิ่มฟังก์ชันยูทิลิตี้ที่ทำให้รายการที่ซ้อนกันเรียบ คุณจะใช้สิ่งนี้เพื่อทำให้รายการตัวอย่างเรียบขึ้น

 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, [])

เพิ่มยูทิลิตี้ที่สองที่โหลดตัวอย่างจากไฟล์ที่ระบุ วิธีนี้จะสรุปข้อเท็จจริงที่ว่าตัวอย่างถูกกระจายไปทั่วหลายไฟล์ โดยส่งคืนเพียงตัวสร้างเดียวสำหรับตัวอย่างทั้งหมด สำหรับแต่ละตัวอย่าง ป้ายกำกับคือดัชนีของไฟล์ เช่น หากคุณเรียก get_all_samples('a.json', 'b.json') ตัวอย่างทั้งหมดใน a.json จะมีเลเบล 0 และตัวอย่างทั้งหมดใน b.json จะมีเลเบล 1

 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

ถัดไป เพิ่มยูทิลิตี้ที่เข้ารหัสตัวอย่างโดยใช้แบบจำลองแบบถุงคำ นี่คือตัวอย่าง: สมมติว่าเรารวบรวมสองตัวอย่าง

  1. เครือข่าย wifi A ที่ระดับ 10 และเครือข่าย wifi B ที่ระดับความแรง 15
  2. เครือข่าย wifi B ที่ระดับ 20 และเครือข่าย wifi C ที่ระดับ 25

ฟังก์ชันนี้จะสร้างรายการตัวเลขสามตัวสำหรับแต่ละตัวอย่าง: ค่าแรกคือความแรงของเครือข่าย wifi A ค่าที่สองสำหรับเครือข่าย B และค่าที่สามสำหรับ C รูปแบบคือ [A, B, C ].

  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]

โดยใช้ยูทิลิตี้ทั้งสามด้านบนนี้ เราสังเคราะห์กลุ่มตัวอย่างและป้ายกำกับ รวบรวมตัวอย่างและป้ายกำกับทั้งหมดโดยใช้ get_all_samples กำหนดรูปแบบที่สอดคล้องกันโดย ordering เพื่อเข้ารหัสตัวอย่างทั้งหมดโดยใช้ one-hot จากนั้นใช้การเข้ารหัส one_hot กับตัวอย่าง สุดท้าย สร้าง data และ label เมทริกซ์ X และ Y ตามลำดับ

 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

ฟังก์ชันเหล่านี้ทำให้ไปป์ไลน์ข้อมูลสมบูรณ์ ต่อไป เราจะสรุปการทำนายและประเมินแบบจำลอง เริ่มต้นด้วยการกำหนดวิธีการทำนาย ฟังก์ชันแรกจะทำให้เอาต์พุตของโมเดลของเราเป็นปกติ เพื่อให้ผลรวมของค่าทั้งหมดรวมเป็น 1 และค่าทั้งหมดไม่เป็นค่าลบ เพื่อให้แน่ใจว่าผลลัพธ์ที่ได้คือการแจกแจงความน่าจะเป็นที่ถูกต้อง ที่สองประเมินแบบจำลอง

 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

นี้สรุปอรรถประโยชน์การคาดการณ์และการประเมินผลของเรา หลังจากยูทิลิตี้เหล่านี้ ให้กำหนดฟังก์ชัน main ที่จะรวบรวมชุดข้อมูล ฝึกฝน และประเมินผล เริ่มต้นด้วยการอ่านรายการอาร์กิวเมนต์จากบรรทัดคำสั่ง sys.argv ; เหล่านี้เป็นห้องที่จะรวมในการฝึกอบรม จากนั้นสร้างชุดข้อมูลขนาดใหญ่จากห้องที่ระบุทั้งหมด

 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)

ใช้การเข้ารหัสแบบ one-hot กับฉลาก การ เข้ารหัสแบบใช้ครั้ง เดียวจะคล้ายกับรูปแบบคำศัพท์ด้านบน เราใช้การเข้ารหัสนี้เพื่อจัดการกับตัวแปรหมวดหมู่ สมมติว่าเรามีป้ายกำกับที่เป็นไปได้ 3 รายการ แทนที่จะติดป้ายกำกับ 1, 2 หรือ 3 เราติดป้ายกำกับข้อมูลด้วย [1, 0, 0], [0, 1, 0] หรือ [0, 0, 1] สำหรับบทช่วยสอนนี้ เราจะสำรองคำอธิบายว่าเหตุใดการเข้ารหัสแบบใช้ครั้งเดียวจึงมีความสำคัญ ฝึกโมเดล และประเมินทั้งชุดฝึกและชุดตรวจสอบ

 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)

พิมพ์ความแม่นยำทั้งสอง และบันทึกโมเดลลงดิสก์

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

ที่ส่วนท้ายของไฟล์ ให้เรียกใช้ฟังก์ชัน main

 if __name__ == '__main__': main()

บันทึกและออก. ตรวจสอบอีกครั้งว่าไฟล์ของคุณตรงกับรายการต่อไปนี้:

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

บันทึกและออก. จำชื่อห้องที่ใช้ด้านบนเมื่อบันทึกตัวอย่าง 20 ตัวอย่าง ใช้ชื่อนั้นแทน bedroom ด้านล่าง ตัวอย่างของเราคือ bedroom . เราใช้ -W ignore เพื่อละเว้นคำเตือนจากจุดบกพร่องของ LAPACK

 python -W ignore model/train.py bedroom

เนื่องจากเราได้รวบรวมตัวอย่างการฝึกอบรมไว้สำหรับหนึ่งห้องเท่านั้น คุณควรเห็นการฝึกอบรมและการตรวจสอบความถูกต้อง 100%

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

ต่อไป เราจะเชื่อมโยงสคริปต์การฝึกอบรมนี้กับแอปเดสก์ท็อป

ขั้นตอนที่ 5: เชื่อมโยงสคริปต์รถไฟ

ในขั้นตอนนี้ เราจะฝึกโมเดลใหม่โดยอัตโนมัติทุกครั้งที่ผู้ใช้รวบรวมกลุ่มตัวอย่างใหม่ เปิด scripts/observe.js

 nano scripts/observe.js

หลังจากนำเข้า fs แล้ว ให้นำเข้า spawner โปรเซสลูกและยูทิลิตี้

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

ในฟังก์ชัน ui ให้เพิ่มการเรียกต่อไปนี้เพื่อ retrain เมื่อสิ้นสุดตัวจัดการการเสร็จสิ้น

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

หลังจากฟังก์ชัน ui ให้เพิ่มฟังก์ชัน retrain ต่อไปนี้ สิ่งนี้ทำให้เกิดกระบวนการย่อยที่จะเรียกใช้สคริปต์หลาม เมื่อเสร็จสิ้น กระบวนการจะเรียกตัวจัดการความสมบูรณ์ เมื่อล้มเหลว มันจะบันทึกข้อความแสดงข้อผิดพลาด

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

บันทึกและออก. เปิด scripts/utils.js

 nano scripts/utils.js

เพิ่มยูทิลิตี้ต่อไปนี้เพื่อดึงชุดข้อมูลทั้งหมดใน data/

 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 }

บันทึกและออก. สำหรับการสิ้นสุดของขั้นตอนนี้ ให้ย้ายไปยังตำแหน่งใหม่ ควรมีกำแพงกั้นระหว่างตำแหน่งเดิมกับตำแหน่งใหม่ของคุณ ยิ่งมีอุปสรรคมากเท่าไร แอปเดสก์ท็อปของคุณก็จะยิ่งทำงานได้ดีขึ้นเท่านั้น

เรียกใช้แอปเดสก์ท็อปของคุณอีกครั้ง

 npm start

เช่นเดียวกับก่อนหน้านี้ ให้รันสคริปต์การฝึกอบรม คลิกที่ "เพิ่มห้อง"

หน้าแรกด้วยปุ่ม
หน้าแรกที่มีปุ่ม "เพิ่มห้องใหม่" (ตัวอย่างขนาดใหญ่)

พิมพ์ชื่อห้องที่แตกต่างจากห้องแรกของคุณ เราจะใช้ living room

เพิ่มหน้าห้องใหม่
โหลดหน้า "เพิ่มห้องใหม่" (ตัวอย่างขนาดใหญ่)

คลิก “เริ่มการบันทึก” และคุณจะเห็นสถานะต่อไปนี้ “กำลังฟัง wifi…”

“เพิ่มห้องใหม่” เริ่มบันทึกสำหรับห้องที่สอง (ตัวอย่างขนาดใหญ่)

เมื่อบันทึกตัวอย่างทั้งหมด 20 ตัวอย่าง แอปของคุณจะตรงกับรายการต่อไปนี้ สถานะจะขึ้นว่า "เสร็จสิ้น โมเดลฝึกหัด…”

เสร็จสิ้นการบันทึก2
หน้า “เพิ่มห้องใหม่” หลังจากบันทึกสำหรับห้องที่สองเสร็จสมบูรณ์ (ตัวอย่างขนาดใหญ่)

ในขั้นตอนต่อไป เราจะใช้แบบจำลองที่ได้รับการฝึกใหม่นี้เพื่อคาดการณ์ห้องที่คุณอยู่ในทันที

ขั้นตอนที่ 6: เขียนสคริปต์ประเมินผล Python

ในขั้นตอนนี้ เราจะโหลดพารามิเตอร์โมเดลที่ฝึกไว้ล่วงหน้า สแกนหาเครือข่าย wifi และคาดการณ์ห้องตามการสแกน

เปิด model/eval.py

 nano model/eval.py

นำเข้าไลบรารีที่ใช้และกำหนดไว้ในสคริปต์ล่าสุดของเรา

 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

กำหนดยูทิลิตี้เพื่อแยกชื่อของชุดข้อมูลทั้งหมด ฟังก์ชันนี้ถือว่าชุดข้อมูลทั้งหมดถูกจัดเก็บไว้ใน data/ เป็น <dataset>_train.json และ <dataset>_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}))

กำหนดฟังก์ชัน main และเริ่มต้นด้วยการโหลดพารามิเตอร์ที่บันทึกไว้จากสคริปต์การฝึก

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

สร้างชุดข้อมูลและคาดการณ์

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

คำนวณคะแนนความเชื่อมั่นตามความแตกต่างระหว่างความน่าจะเป็นสองอันดับแรก

 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)

สุดท้าย แยกหมวดหมู่และพิมพ์ผลลัพธ์ ในการสรุปสคริปต์ ให้เรียกใช้ฟังก์ชัน main

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

บันทึกและออก. ตรวจสอบรหัสของคุณอีกครั้งว่าตรงกับสิ่งต่อไปนี้ (ซอร์สโค้ด):

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

ต่อไป เราจะเชื่อมต่อสคริปต์การประเมินนี้กับแอปเดสก์ท็อป แอปเดสก์ท็อปจะเรียกใช้การสแกน wifi อย่างต่อเนื่องและอัปเดต UI ด้วยห้องที่คาดคะเน

ขั้นตอนที่ 7: เชื่อมต่อการประเมินกับแอปเดสก์ท็อป

ในขั้นตอนนี้ เราจะอัปเดต UI ด้วยการแสดง "ความมั่นใจ" จากนั้น สคริปต์ NodeJS ที่เกี่ยวข้องจะเรียกใช้การสแกนและการคาดคะเนอย่างต่อเนื่อง โดยอัปเดต UI ตามนั้น

เปิด static/index.html

 nano static/index.html

เพิ่มบรรทัดเพื่อความมั่นใจหลังชื่อเรื่องและหน้าปุ่ม

 <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 แต่ก่อนส่วนท้ายของ body ให้เพิ่มสคริปต์ใหม่ predict.js

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

บันทึกและออก. เปิด scripts/predict.js

 nano scripts/predict.js

อิมพอร์ตยูทิลิตี NodeJS ที่จำเป็นสำหรับระบบไฟล์ ยูทิลิตี และโปรเซสลูก

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

กำหนดฟังก์ชัน predict ซึ่งเรียกใช้กระบวนการโหนดแยกต่างหากเพื่อตรวจจับเครือข่าย wifi และกระบวนการ Python แยกต่างหากเพื่อทำนายห้อง

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

หลังจากที่กระบวนการทั้งสองเกิดขึ้นแล้ว ให้เพิ่มการเรียกกลับไปยังกระบวนการ Python สำหรับทั้งความสำเร็จและข้อผิดพลาด การโทรกลับที่ประสบความสำเร็จจะบันทึกข้อมูล เรียกใช้การโทรกลับที่เสร็จสิ้น และอัปเดต UI ด้วยการคาดการณ์และความมั่นใจ การเรียกกลับข้อผิดพลาดจะบันทึกข้อผิดพลาด

 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 แบบวนซ้ำตลอดไป

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

เป็นครั้งสุดท้าย เปิดแอปเดสก์ท็อปเพื่อดูการคาดคะเนแบบสด

 npm start

ทุกๆ วินาที การสแกนจะเสร็จสมบูรณ์ และอินเทอร์เฟซจะได้รับการอัปเดตด้วยความมั่นใจล่าสุดและห้องที่คาดการณ์ไว้ ยินดีด้วย; คุณได้เสร็จสิ้นเครื่องตรวจจับห้องอย่างง่ายโดยอิงตามเครือข่าย WiFi ทั้งหมดในช่วง

การสาธิต
บันทึกตัวอย่างภายในห้อง 20 ตัวอย่าง และอีก 20 ตัวอย่างในโถงทางเดิน เมื่อเดินกลับเข้าไปข้างใน สคริปต์จะทำนาย "โถงทางเดิน" ตามด้วย "ห้องนอน" ได้อย่างถูกต้อง (ตัวอย่างขนาดใหญ่)

บทสรุป

ในบทช่วยสอนนี้ เราได้สร้างโซลูชันโดยใช้เดสก์ท็อปของคุณเท่านั้นในการตรวจจับตำแหน่งของคุณภายในอาคาร เราสร้างแอปเดสก์ท็อปอย่างง่ายโดยใช้ Electron JS และใช้วิธีการเรียนรู้ของเครื่องอย่างง่ายบนเครือข่าย WiFi ทุกเครือข่าย สิ่งนี้เป็นการปูทางสำหรับแอปพลิเคชั่นอินเทอร์เน็ตของสิ่งต่าง ๆ โดยไม่ต้องใช้อาร์เรย์ของอุปกรณ์ที่มีค่าใช้จ่ายสูงในการบำรุงรักษา (ค่าใช้จ่ายไม่ใช่ในแง่ของเงิน แต่ในแง่ของเวลาและการพัฒนา)

หมายเหตุ : คุณสามารถดูซอร์สโค้ดได้อย่างครบถ้วนบน Github

เมื่อเวลาผ่านไป คุณอาจพบว่าช่องสี่เหลี่ยมที่น้อยที่สุดนี้ไม่ได้ทำงานอย่างน่าทึ่ง ลองหาสถานที่สองแห่งภายในห้องเดียวหรือยืนอยู่ที่ประตู สี่เหลี่ยมที่เล็กที่สุดจะมีขนาดใหญ่ไม่สามารถแยกแยะระหว่างเคสขอบได้ เราสามารถทำได้ดีกว่านี้หรือไม่? ปรากฎว่าเราทำได้ และในบทเรียนต่อๆ ไป เราจะใช้ประโยชน์จากเทคนิคอื่นๆ และพื้นฐานของแมชชีนเลิร์นนิงเพื่อประสิทธิภาพที่ดีขึ้น บทช่วยสอนนี้ทำหน้าที่เป็นเตียงทดสอบอย่างรวดเร็วสำหรับการทดลองที่จะมาถึง