A fogyásom elemzése gépi tanulással

Hogyan építettem egy logisztikai regressziós osztályozót a semmiből a Pythonnal, hogy megjósoljam a fogyásomat

Khanh Nguyen

2019. február 19. · 30 perc olvasás

Ha meg szeretné tekinteni a kódot, amelyet ehhez a projekthez írtam, megnézheti a Github repo-t

gépi

A súlycsökkentő utat 2018 elején kezdtem, a „fogyás = diéta + testmozgás” gyakran idézett tanácsát követve. A diéta oldalán elkezdtem nyomon követni a napi élelmiszer-fogyasztást (ételmérleg használatával és a kalóriák rögzítésével a Loseit alkalmazáson keresztül). A testedzés oldalán elkezdtem követni a Couch to 5K programot, és a mai napig négy 5K-s, egy 10K-s, és néhány héttel ezelőtti félmaratonon végeztem. Végül minden reggel mérlegeltem magam közvetlenül ébredés után, és ugyanabban a Loseit alkalmazásban rögzítettem a súlyomat.

Aki fogyni próbál, elkerülhetetlenül eléri a fogyás fennsíkját, ahol a kezdeti gyors fogyás lassulni kezd. Személy szerint egy nagy fennsíkot értem el közvetlenül a május eleji nyaralásom után. Ezt követően közel három hónapig felhagytam a fejlődésem nyomon követésével. Csak miután lecseréltem haldokló Pebble okosórámat egy Amazfit Bip-re, visszanyertem némi motivációt, hogy megnyomjam a folytatás gombot, részben mivel elkezdhettem követni a lépéseimet az új órával. A súlyom azonban tovább folytatódott, és további két hónapos frusztráló súlyingadozások után (lásd alább a pontozott régiót) abbahagytam a súlyom és a kalóriák nyomon követését. Ez tavaly november volt.

Most 2019 van, és amint a Tet (vietnami újév) nemrégiben véget ért, úgy döntöttem, hogy alaposabban megvizsgálom a két hónapos időszak alatt összegyűjtött adatokat, remélve, hogy érdekes összefüggéseket fedezek fel fogyásom és a nyomon követett kalóriák között./lépéseket, hogy a korábbinál hatékonyabb fogyókúrát tudjak elkészíteni.

Adatgyűjtés

Kalóriák: exportáltam a Loseit-fiókomból CSV-fájlként. Valamilyen furcsa okból a Loseit csak egy héten keresztül engedélyezi a kalóriaadatok exportálását, de ezek összekapcsolása nem jelent semmit egy gyors Python-szkript segítségével. Minden dátumnak megvan a megfelelő kalóriaszáma az összes, aznap naplózott ételből, valamint egy kalória „költségvetés”, amelyet az alkalmazás számított ki számomra aznapi testsúlyom és az eredetileg megadott súlycsökkentési cél (0,75 kg lefogyás) alapján. /hét).

Lépések: az Amazfit Bip okosórám Android-alkalmazása csak akkor engedélyezi az adatok exportálását, ha valaki szőrös megoldást alkalmaz harmadik féltől származó eszközökkel. Ezért a lépésadatok megszerzésének leggyorsabb módja az volt, hogy manuálisan végiglapoztam a dátumok tucatját (ezen a két hónapos időszakon belül) a telefonomon, és az egyes dátumokra vonatkozó lépéseket beírtam egy CSV-fájlba. Egyáltalán nem elegáns, de hé, működik!

Súly: szerencsére a Loseit webhely lehetővé teszi számomra, hogy az összes rögzített súlyomat - és a felvételük dátumát - egyetlen CSV fájlként exportálhassam.

Miután dátum szerint összevontam ezt a 3 adatforrást, a kalóriák + lépések + súlyadatok végül csak 46 dátumra vonatkoznak ebből a két hónapos időszakból. Nyilvánvalóan hanyagoltam ebből a három adatból legalább egy felvételét jó néhány dátumban (sok közülük hétvégén, nyilvánvaló okokból).

Adattranszformáció

Ebből a három nyers adatforrásból további három adatmezőt számolnak ki:

Többlet = elfogyasztott kalóriák - kalóriaköltségvetés. A pozitív többlet azt jelenti, hogy több kalóriát ettem, mint az aznapra megengedett költségvetés, és fordítva. Úgy döntök, hogy többlet kalóriát használok az elfogyasztott nyers kalóriák helyett, mivel az alkalmazás kalóriaköltsége természetesen változik, ahogy a súlyom emelkedik és csökken, így a kalóriatöbblet (amely figyelembe veszi az említett költségvetést) az étkezési szokásaim pontosabb mértéke, mint önmagában a kalóriák.

Súlygyarapodás = holnapi súly - ma súly. Egy adott dátum pozitív súlygyarapodása azt jelenti, hogy aznap meghíztam (duh!), És fordítva.

Súlygyarapodás állapota: a pozitív súlygyarapodást 1-nek, a negatív vagy nulla súlygyarapodást pedig 0-nak jelölném. Úgy döntök, hogy a bináris súlygyarapodási státust használom - függetlenül attól, hogy híztam-e vagy sem -, nem pedig a szemcsésebb súlygyarapodási mennyiség helyett. a 0,5 kg-os és 0,3 kg-os hízás meglehetősen kontraproduktív, már csak azért is, mert a súlygyarapodás mértékét a kalóriák és a lépések mellett számos tényező is befolyásolhatja (például vízfogyasztás, étkezési idő stb.).

Adatok vizualizálása

Amikor a kalóriatöbbletet és a napi lépések számát ábrázolom az adott napi súlygyarapodási állapotommal (lásd alább az első két panelt), úgy tűnik, nincsenek nyilvánvaló minták arról, hogy ez a két állítólag fontos tényező hogyan képes megjósolni, hogy híznék-e vagy sem.

Mindazonáltal, ha mind a kalóriatöbbletet, mind az együttes lépéseket megtervezzük (a jobb oldali panel fent), nagyon érdekes minták jelennek meg! Például rögtön elmondhatom, hogy a lépések számának szempontjából két különböző dátumcsoport létezik: azok, amelyek 5000 lépés alatt vannak (az "alapvonali" lusta napjaim), amelyek szépen fekszenek egy vízszintes vonal körül, és azok, amelyek 5000 felett vannak ( aktív napok), nagyrészt a futásomnak köszönhetően. A kalóriatöbblet tekintetében három fő megfigyelés van:

  1. Lusta napjaimban, ha a kalória költségvetésemnél nagyobb mértékben eszem, másnap rossz hír lesz, amikor rálépek a mérlegre. Volt néhány csoda, például az az egy nap, amikor majdnem 1500 kalóriát ettem a határértékem fölött, és másnap nem hízott fel, de ezek kevés.
  2. Viszont, ha lusta napjaim alatt a kalória-költségvetési korlátom alatt eszem, akkor sem vagyok teljesen világos: van néhány nap, amikor jó fiú voltam és megettem a salátáimat (átvitt értelemben), mégis felszedett súly. Ez arra utal, hogy még konzervatívabbnak kell lennem a lusta napok étkezése során.
  3. Azonban aktív napjaimban úgy tűnik, hogy megengedhetem magamnak, hogy többet fogyasszak, mint a kalóriahatár, mivel több olyan aktív nap van, amikor többet ettem, mint amit a határ megengedett, és még mindig nem hízott.

Ezek a megfigyelések azt sugallják, hogy a Loseit alkalmazás alapértelmezett korlátjának használata helyett a napi aktivitásomat (lépésszámlálás formájában) kellene figyelembe vennem. Például a fenti többletlépéses ábrán meg tudok rajzolni egy egyenes vonalat, amely nagyrészt elválasztja a súlygyarapodási napjaimat (piros) a fogyási napjaimtól (zöld). Ezután felhasználhatom ezt a lineáris határt, hogy tájékoztassam a súlycsökkentési stratégiámat, vagyis hogyan maradhatok a határ súlycsökkentő oldalán a másik oldal helyett. A gépi tanulás nyelvén ez egyenértékű egy lineáris osztályozó felépítésével az adataim bináris osztályozásához (a súlygyarapodás súlycsökkenésből történő osztályozása).

Melyik lineáris osztályozót kell használni?

Számos általános módszer létezik egy lineáris osztályozó létrehozására adatokból, például logisztikai regresszió, lineáris diszkrimináns elemzés vagy támogató vektorgép. Ehhez a projekthez azonban logisztikai regressziót fogok használni, mert:

  • Könnyen értelmezhető: például a besorolási határ egyenlete könnyen megszerezhető a logisztikai regressziós együtthatókból.
  • Könnyű megvalósítani: nagyon fontos ok, mivel ennek a projektnek egy másik célja, hogy gépi tanulási projektet valósítsak meg anélkül, hogy már meglévő könyvtárakat (például scikit-learn) használnék. Amint később megmutatom, a logisztikai regresszió tanulási algoritmusának alapja mindössze 3 kódsorban valósítható meg!

Ezzel a modellválasztással nézzük meg, hogyan lehet a logisztikai regressziót megvalósítani és értelmezni az adataim segítségével. Ehhez azonban meg kell vizsgálnunk néhány matematikát arról, hogy a logisztikai regresszió osztályozó hogyan működik, és hogyan lehet megtanulni az adatokból.

Felhívjuk figyelmét, hogy ez a matematikai áttekintés elsősorban egy közös jelölés létrehozására szolgál, hogy ugyanazon az oldalon legyünk, mielőtt az algoritmus a matematikai egyenletekből megvalósulna. Ha meg akarja érteni, hogyan származnak ezek az egyenletek és a mögöttük rejlő intuíció, a blogbejegyzés végén további forrásokhoz kapcsoltam, amelyek sokkal jobban képesek elvégezni ezeket a munkákat, mint én.

Megjósolja a súlygyarapodás valószínűségét a jellemzők alapján

A logisztikai regresszióban az az előrejelzett valószínűség, hogy hízni fogok egy adott napon, a sigmoid függvény, amelyet jellemzőim (kalória többlet és lépésszám) súlyozott lineáris kombinációjára alkalmaznak, plusz egy állandó elfogási idő. Ezt a kapcsolatot matematikailag a következőképpen fejezik ki:

  • y (i): súly ismét az i dátum állapota (1 = súlygyarapodás, 0 = fogyás)
  • P (y (i) = 1): megjósolt valószínűség, hogy hízni fogok az i dátumon
  • x (i): a vonatkozó jellemzők (kalóriatöbblet és lépésszám) megfigyelt értékei az i dátumon
  • θ: a vonatkozó jellemzők regressziós együtthatói/súlyai ​​(adatokból kell megtudni) *

* Ne feledje, hogy felvettem egy további funkciót (x_intercept), amely mindig egyenlő lesz 1-vel, így az elfogó kifejezés (θ_intercept) megtanulható a két létező jellemző együtthatóival együtt.

Ezért, ha θ-ket megtanultam az adatokból, logisztikai regresszióval lehet osztályozni, hogy hízni fogok-e egy adott napon - figyelembe véve a kalóriatöbbletet és a lépésszámot - annak ellenőrzésével, hogy meghaladja-e annak a valószínűségét, hogy hízni fogok ezen a napon egy bizonyos küszöb (általában 50%).

Hogyan tanulják meg azokat

A regressziós együtthatókat (θ-ek) az edzésadataim megfigyelésének valószínűségének logaritmusának maximalizálása révén tanuljuk meg (más néven log-likelihood). A log-valószínûség képlete:

  • L: az edzés adatainak log-valószínűsége (m adatpont)
  • y (i): az i dátum valódi súlygyarapodási állapota (1 = súlygyarapodás, 0 = fogyás)
  • P (y (i) = 1): megjósolt valószínűség, hogy hízni fogok az i dátummal (a korábbi sigmoid függvényből nyertem)

A sigmoid függvényből a θ-k különböző értékei különböző előre jelzett súlygyarapodási valószínűségeket eredményeznek - P (y (i) = 1) -, és ennek eredményeként eltérő log-valószínűséggel. Ezért a cél megtalálni azokat a θ-eket, amelyek maximalizálják az edzési adataim log-valószínűségét, vagyis azokat a θ-eket, amelyek a legjobban „megmagyarázzák” az edzési adataimat.

A log-valószínűség maximalizálása kötegelt gradiens emelkedéssel

Az egyik egyszerű algoritmus az θ-k megtalálásához, amelyek maximalizálják az edzésadataim log-valószínűségét, a kötegelt gradiens emelkedés, amelyet az alábbiakban ismertetünk:

0. lépés: Inicializálja a θ értékeinek néhány értékét (θ_intercept, θ_többlet, θ_step)

1. lépés: Az egyes i edzési adatpontokhoz számítsa ki a súlygyarapodás valószínűségét az adatpont jellemzőértékeinek felhasználásával (x_intercept *, x_surplus, x_step), és a 0. lépésben inicializált θ értékeit. Ez az ismert sigmoid függvény segítségével történik:

* visszahívás x_intercept = 1

2. lépés: Minden j tulajdonságra - elfogás/többlet/lépés - keresse meg a log-valószínûség részleges deriváltját az adott jellemzõ θ -jéhez viszonyítva az alábbi egyenlet segítségével:

  • ∂L/∂θⱼ: a log-valószínűség részleges deriváltja a j jellemző feature -jához képest
  • y (i): az igazi dátum ismét az i dátum állapota (1 = súlygyarapodás, 0 = fogyás)
  • P (y (i) = 1): az i dátum súlygyarapodásának várható valószínűsége (az 1. lépéstől)
  • xⱼ (i): A j jellemző megfigyelt értéke (elfogás/többlet/lépés) az i dátumon, az x_intercept (i) = 1 bármely i-nél

3. lépés: Minden j jellemzőnél frissítse θ-jét a log-valószínőség valószínűségének részleges deriváltjával az θ vonatkozásában (a 2. lépéstől), szorozva egy kis állandóval (más néven a tanulási sebesség α-val):

Ez a tanulási sebesség szabályozza, hogy az algoritmus milyen gyorsan konvergáljon a log-valószínûség maximumáig, vagy akár konvergál-e egyáltalán (további részletekért lásd az algoritmus konvergenciájának megjelenítésérõl szóló késõbbi részt).

4. lépés: Ezekkel a frissített θ-kkel ismételje meg az 1. és a 3. lépést a konvergenciáig. A konvergencia tesztelésének egyik módja az, hogy megnézzük, hogy a log-valószínűség stabil értékre konvergál-e, azaz elérte-e a valószínű maximumot.

Batch vs. sztochasztikus gradiens emelkedés

Az összegző jel a 2. lépésben - összegzés (y (i) - P (y (i) = 1)) * xⱼ (i) Az összes adatpont felett i a részleges derivált kiszámításához - ez az oka annak, hogy ez a gradiens emelkedő algoritmus kötegelt változatú, mivel minden részleges deriváltat a képzési adatok összes adatpontjának felhasználásával számolnak. Ezen összegző jel nélkül, vagyis a részleges deriváltat csak egy adatpont felhasználásával számítják ki; feltételezve, hogy az adatpontot véletlenszerűen választják meg, a gradiens emelkedő algoritmust sztochasztikusnak nevezzük.

Ennél a projektnél a kötegelt gradiens emelkedés megvalósítását választom, mivel az edzésadataim nagyon kicsiek (csak 46 adatpont), így nincs probléma a teljes képzési adatkészlet egyszerre történő felhasználásával a részleges derivatívák kiszámításához. Egy másik ok az, hogy a kötegelt gradiens emelkedés könnyebben megvalósítható a numpy vektorizált műveleteinek használatával (a végrehajtás módját lásd alább).

Adatok előzetes feldolgozása

Az előző adattáblázatomból a kalóriatöbblet és a lépésszámláló oszlopokat használom jellemzőként, a súlygyarapodás állapot oszlopot pedig címkeként (lásd balra) a logisztikai regressziós osztályozó képzéséhez.

Mielőtt azonban megvalósítanám a kötegelt gradiens emelkedési algoritmust ezeken az adatokon, először:

  1. Adjon hozzá 1-es oszlopot képzési adataimhoz, hogy képviseljék az x_intercept tulajdonság értékeit.
  2. Átméretezem a kalóriatöbbletet és a lépésszámlálási jellemzőket úgy, hogy (a) kivonjuk az egyes jellemző oszlopokat az átlagukkal, és (b) elosztjuk a szórásukkal. Ez két okból történik:

A fenti két lépés után az (X) funkciómátrixom átalakul egy 2-D dimenziós tömbbe (46, 3), és a címkevektorom (y) egy 1-D numpy tömbgé (46).

Végezze el a kötegelt gradiens emelkedést

0. lépés: A θ inicializálása

Minden my-t inicializálom 0,5-re. Az eredmény egy (3) dimenziós 1-D számú tömb téta

1. lépés: Számítsa ki az egyes adatpontokhoz a súlygyarapodás valószínűségét a 0. lépésből származó θ-ek és a sigmoid függvény segítségével

Itt hasznosak a numpy vektorizált műveletei, mivel ahelyett, hogy kiszámítaná az egyes adatpontok súlygyarapodásának valószínűségét, a numpy minden adatpontra egyszerre teszi: