{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import datasets\n",
    "from sklearn import model_selection\n",
    "from sklearn import preprocessing\n",
    "from sklearn import linear_model\n",
    "from sklearn import metrics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tehnike za rad sa nebalansiranim skupovima podataka"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Za skup podataka kazemo da je **nebalansiran** ukoliko ima znacajno vise instanci jedne klase u odnosu na drugu klasu (ili klase). U realnim problemima klasifikacije vrlo cesto su podaci sa kojima se raspolaze u znatnoj meri nebalansirani usled retkosti fenomena koji se modeluje. Na primer, kod bankarskih transakcija samo jedan mali procenat transakcija nije regularan i podleze zakonskim procedurama. Slicno, kada su retke bolesti u pitanju, mali je broj pacijenata za koje su mogu pronaci medicinski podaci. Medjutim, postoje i scenariji kada je nebalansiranost podatak posledica loseg odabira uzorka. Na primer, u anketi je ispitivana vecinski mladja populacija, a ne opste mnjenje.\n",
    "\n",
    "Kako ne bismo dobili model koji sve/vecinu instanci klasifikuje u vecinsku klasu i koji samim tim nije upotrebljiv, potrebno je da na neki nacin smanjimo uticaj nebalansiranosti klasa na proces ucenje modela. Postoje razlicite tehnike kojima se to moze postici.\n",
    "\n",
    "**Eliminacija instanci vecinske klase (eng. undersampling)** $\\hspace{0.2cm}$ Ukoliko je na raspolaganju dovoljan broj instanci manjinske klase, moze se izdvojiti isto toliko instanci vecinske klase i obucavati model na ovako dobijenom balansiranom skupu. Izbor instanci vecinske klase moze da bude nasumican.\n",
    "\n",
    "**Generisanje novih instanci manjinske klase (eng. oversampling)** $\\hspace{0.2cm}$ Ukoliko nije na raspolaganju dovoljan broj instanci manjinske klase, moze se probati sa generisanjem novih instanci istih/veoma slicnih vrednosti atributa kao postojece instance. Postoje razlicite tehnike koje na razlicite nacine simuliraju generisanje instanci iz iste distribucije.\n",
    "\n",
    "<img src=\"assets/under_and_over_sampling.png\" width=700>\n",
    "\n",
    "Oba pomenuta pristupa imaju svoje prednosti i mane. Eliminisanjem instanci moze doci do gubitka vaznih informacija, dok se generisanjem novih instanci povecava rizik od preprilagodjavanja (kada se multipliciraju postojece instance) ili moze doci do ubacivanja dodatnog suma u skup podataka (kada se generisu upotpunosti nove instance). Takodje, u oba slucaja nasumican izbor moze biti pristrasan i moze uticati na performanse klasifikatora. \n",
    "\n",
    "**Pridruzivanje razlicitih tezina instancama razlicitih klasa (eng. instance-weighting)** $\\hspace{0.2cm}$ U osnovi ovog pristupa je ideja da se razlicito penalizuju greske modela na instancama manjinske u odnosu na instance vecinske klasi. Vece vrednosti tezina instanci ukazuju modelu da obrati 'vise paznje' na njih prilikom obucavanja.\n",
    "\n",
    "**Ansambli** $\\hspace{0.2cm}$ U ovom pristupu se tezi da se iskoriste svi raspolozivi podaci. Koriste se sve instance manjinske klase i veci broj skupova iste kardinalnosti instanci vecinske klase. Na primer, ako imamo 400 instanci manjinske klase i 2000 instanci vecinske klase, mozemo napraviti 5 balansiranih skupova sa 400 instanci manjinske i 400 instanci vecinske klase. Na svakom od ovih skupova se moze nauciti poseban klasifikator. Kada je potrebno klasifikovati novu instancu koriste se svi klasifikatori i primenjuje se npr. *princip vecinskog glasanja* - instanca se pridruzuje klasi za koju najveci broj klasifikatora 'glasa'. Kao ocena ovakvog modela (ansambla) moze se uzeti npr. prosek ocena pojedinacnih klasifikatora. O ovom pristupu ce biti vise reci na nekom od narednih casova kada se budemo bavili modelima ovog tipa. \n",
    "\n",
    "**Klasterovanje vecinske klase** $\\hspace{0.2cm}$ Ovaj pristup se zasniva na minimizovanju gubitka informacija o vecinskoj klasi prilikom eliminacije instanci. Vecinska klasa se moze podeliti na *k* klastera, gde *k* predstavlja kardinalnost manjinske klase. Zatim se na osnovu centroida ovako dobijenih klastera moze generisati novi skup koji predstavlja vecinsku klasu i koji se dalje moze koristiti u obucavanju klasifikatora. I o ovom pristupu ce biti vise reci na nekom od narednih casova kada se budemo bavili problemom klasterovanja. \n",
    "\n",
    "U svim prethodno navedenim pristupima moze se probati i sa razlicitim razmerama manjinske i vecinske klase, npr 1:2, 1:3, 2:3 i slucno. Na taj nacin se ne eliminise u potpunosti, ali se znacajno ublazava nebalansiranost klasa, pritom umanjujuci negativne strane ovih tehnika na ucenje modela."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Evaluacija modela obucavanih na nebalansiranim skupovima podataka"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Za evaluaciju modela koji su obucavani na nebalansiranim skupovima podataka nije pozeljno koristiti tacnost. Model koji uvek (ili skoro uvek) predvidja vecinsku klasu imace visoku tacnost na datom skupu podataka, a da pritom nije naucio nista iz podataka (osim koja je klasa vecinska). Zato je pozeljnije koristiti preciznost, odziv i F1 meru.\n",
    "\n",
    "Jos jedna mera koja je prikladna za slucaj binarne klasifikacije sa nebalansiranim klasama je **AUC mera** iliti **povrsina ispod ROC krive** (eng. Area Under the ROC Curve, AUC). Pretpostavka je da klasifikator pociva na nekom modelu koji razlicitim instancama pridruzuje neke skorove koji se zatim prevode u klasu zaokruzivanjem u odnosu na neki prag (npr. kod logisticke regresije predvidjanje klase dobija se zaokruzivanjem vrednosti sigmoidne funkcije u odnosu na prag 0.5, podrazumevano). **ROC kriva** je kriva koja opisuje odnos udela lazno pozitivnih instanci (eng. false positive rate, FPR) i udela stvarno pozitivnih instanci (eng. true positive rate, TPR) za razlicite vrednosti pragova. \n",
    "\n",
    "$$FPR = \\frac{FP}{FP + TN} \\hspace{0.5cm} TPR = \\frac{TP}{TP + FN}$$\n",
    "\n",
    "Za svaku vrednost praga na skorovima koje klasifikator daje definisana je jedna tacka na ovoj krivoj. Variranjem praga od maksimalnog skora do minimalnog skora (na datim instancama) dobija se kriva koja spaja koordinatni pocetak sa tackom (1, 1). Ocekivano, sa spustanjem praga povecava se broj instanci koje se klasifikuju kao pozitivne, a samim tim rastu i udeli stvarno pozitivnih i lazno pozitivnih instanci.\n",
    "\n",
    "Karakteristika dobrih modela je da TPR brze raste od FPR, odnosno da se za male vrednosti FPR-a mogu dobiti visoke vrednosti TPR-a. To znaci da za pogodan odabir praga ovaj model prilikom predvidjanja pozitivne klase puno pogadja a malo gresi, a samim tim i pri predvidjanju negativne klase. Dakle, model je utoliko bolji sto mu ROC kriva ima izvijeniji oblik u smeru ka tacki $(0, 1)$ koja odgovara savrsenom klasifikatoru. Sa druge strane, random klasifikator (najlosiji model) ocekivano podjednako pogadja koliko i gresi prilikom predikcija (TPR = FPR) nezavisno od postavljenog praga, tako da njemu odgovara ROC kriva sa tackama po dijagonali od koordinatnog pocetka do $(1, 1)$.\n",
    "\n",
    "<img src=\"assets/ROC_curve.png\" width=300>\n",
    "\n",
    "**NAPOMENA**: Ako se binarni klasifikator ponasa gore od random klasifikatora, tada se invertovanjem njegovih predikcija dobija klasifikator koji je bolji od radnom klasifikatora, tako da se model moze koristiti u tom invertovanom obliku. Iz tog razloga se random klasifikator uzima za najlosiji klasifikator!\n",
    "\n",
    "Umesto iscrtavanja i analiziranja karakteristika ROC krive, kvalitet klasifikatora se kvantitativno izrazava povsinom ispod ROC krive, tj. **AUC merom**. Vrednosti ove mere su iz intervala $[0.5, 1]$, gde vrednost $0.5$ odgovara najlosijem (random) klasifikatoru, a vrednost $1$ savrsenom klasifikatoru. \n",
    "\n",
    "**VAZNO**: Primetimo da se termini FPR i TPR preklapaju sa pojmovima preciznosti i odziva. Tacnije, TPR je isto sto i odziv, dok je FPR u direktnoj vezi sa (1 - preciznost). Zato se AUC mera interpretira jos i kao kolicina preciznosti koju treba zrtvovati da bi se ostvario visok odziv. Takodje, kod profesora u skripti se moze pronaci jos jedna interpretacija AUC mere koja je jos intuitivnija od prethodno pomenutih."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Primer"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[imbalanced-learn](https://imbalanced-learn.org) je Python biblioteka koja pruza podrsku za rad sa nebalansiranim skupovima podataka. Na raspolaganju su sve prethodno pomenute tehnike, ali i mnoge druge."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import imblearn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ucitavanje i pretprocesiranje skupa podataka"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dat je CSV fajl <code>'data/creditcard.csv'</code> koji sadrzi informacije o bankarskim transakcijama. Potrebno je napraviti klasifikator koji razlikuje regularne i neregularne transakcije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv('data/creditcard.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(284807, 31)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Time</th>\n",
       "      <th>V1</th>\n",
       "      <th>V2</th>\n",
       "      <th>V3</th>\n",
       "      <th>V4</th>\n",
       "      <th>V5</th>\n",
       "      <th>V6</th>\n",
       "      <th>V7</th>\n",
       "      <th>V8</th>\n",
       "      <th>V9</th>\n",
       "      <th>...</th>\n",
       "      <th>V21</th>\n",
       "      <th>V22</th>\n",
       "      <th>V23</th>\n",
       "      <th>V24</th>\n",
       "      <th>V25</th>\n",
       "      <th>V26</th>\n",
       "      <th>V27</th>\n",
       "      <th>V28</th>\n",
       "      <th>Amount</th>\n",
       "      <th>Class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.0</td>\n",
       "      <td>-1.359807</td>\n",
       "      <td>-0.072781</td>\n",
       "      <td>2.536347</td>\n",
       "      <td>1.378155</td>\n",
       "      <td>-0.338321</td>\n",
       "      <td>0.462388</td>\n",
       "      <td>0.239599</td>\n",
       "      <td>0.098698</td>\n",
       "      <td>0.363787</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.018307</td>\n",
       "      <td>0.277838</td>\n",
       "      <td>-0.110474</td>\n",
       "      <td>0.066928</td>\n",
       "      <td>0.128539</td>\n",
       "      <td>-0.189115</td>\n",
       "      <td>0.133558</td>\n",
       "      <td>-0.021053</td>\n",
       "      <td>149.62</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.0</td>\n",
       "      <td>1.191857</td>\n",
       "      <td>0.266151</td>\n",
       "      <td>0.166480</td>\n",
       "      <td>0.448154</td>\n",
       "      <td>0.060018</td>\n",
       "      <td>-0.082361</td>\n",
       "      <td>-0.078803</td>\n",
       "      <td>0.085102</td>\n",
       "      <td>-0.255425</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.225775</td>\n",
       "      <td>-0.638672</td>\n",
       "      <td>0.101288</td>\n",
       "      <td>-0.339846</td>\n",
       "      <td>0.167170</td>\n",
       "      <td>0.125895</td>\n",
       "      <td>-0.008983</td>\n",
       "      <td>0.014724</td>\n",
       "      <td>2.69</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1.0</td>\n",
       "      <td>-1.358354</td>\n",
       "      <td>-1.340163</td>\n",
       "      <td>1.773209</td>\n",
       "      <td>0.379780</td>\n",
       "      <td>-0.503198</td>\n",
       "      <td>1.800499</td>\n",
       "      <td>0.791461</td>\n",
       "      <td>0.247676</td>\n",
       "      <td>-1.514654</td>\n",
       "      <td>...</td>\n",
       "      <td>0.247998</td>\n",
       "      <td>0.771679</td>\n",
       "      <td>0.909412</td>\n",
       "      <td>-0.689281</td>\n",
       "      <td>-0.327642</td>\n",
       "      <td>-0.139097</td>\n",
       "      <td>-0.055353</td>\n",
       "      <td>-0.059752</td>\n",
       "      <td>378.66</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1.0</td>\n",
       "      <td>-0.966272</td>\n",
       "      <td>-0.185226</td>\n",
       "      <td>1.792993</td>\n",
       "      <td>-0.863291</td>\n",
       "      <td>-0.010309</td>\n",
       "      <td>1.247203</td>\n",
       "      <td>0.237609</td>\n",
       "      <td>0.377436</td>\n",
       "      <td>-1.387024</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.108300</td>\n",
       "      <td>0.005274</td>\n",
       "      <td>-0.190321</td>\n",
       "      <td>-1.175575</td>\n",
       "      <td>0.647376</td>\n",
       "      <td>-0.221929</td>\n",
       "      <td>0.062723</td>\n",
       "      <td>0.061458</td>\n",
       "      <td>123.50</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2.0</td>\n",
       "      <td>-1.158233</td>\n",
       "      <td>0.877737</td>\n",
       "      <td>1.548718</td>\n",
       "      <td>0.403034</td>\n",
       "      <td>-0.407193</td>\n",
       "      <td>0.095921</td>\n",
       "      <td>0.592941</td>\n",
       "      <td>-0.270533</td>\n",
       "      <td>0.817739</td>\n",
       "      <td>...</td>\n",
       "      <td>-0.009431</td>\n",
       "      <td>0.798278</td>\n",
       "      <td>-0.137458</td>\n",
       "      <td>0.141267</td>\n",
       "      <td>-0.206010</td>\n",
       "      <td>0.502292</td>\n",
       "      <td>0.219422</td>\n",
       "      <td>0.215153</td>\n",
       "      <td>69.99</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 31 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   Time        V1        V2        V3        V4        V5        V6        V7  \\\n",
       "0   0.0 -1.359807 -0.072781  2.536347  1.378155 -0.338321  0.462388  0.239599   \n",
       "1   0.0  1.191857  0.266151  0.166480  0.448154  0.060018 -0.082361 -0.078803   \n",
       "2   1.0 -1.358354 -1.340163  1.773209  0.379780 -0.503198  1.800499  0.791461   \n",
       "3   1.0 -0.966272 -0.185226  1.792993 -0.863291 -0.010309  1.247203  0.237609   \n",
       "4   2.0 -1.158233  0.877737  1.548718  0.403034 -0.407193  0.095921  0.592941   \n",
       "\n",
       "         V8        V9  ...       V21       V22       V23       V24       V25  \\\n",
       "0  0.098698  0.363787  ... -0.018307  0.277838 -0.110474  0.066928  0.128539   \n",
       "1  0.085102 -0.255425  ... -0.225775 -0.638672  0.101288 -0.339846  0.167170   \n",
       "2  0.247676 -1.514654  ...  0.247998  0.771679  0.909412 -0.689281 -0.327642   \n",
       "3  0.377436 -1.387024  ... -0.108300  0.005274 -0.190321 -1.175575  0.647376   \n",
       "4 -0.270533  0.817739  ... -0.009431  0.798278 -0.137458  0.141267 -0.206010   \n",
       "\n",
       "        V26       V27       V28  Amount  Class  \n",
       "0 -0.189115  0.133558 -0.021053  149.62      0  \n",
       "1  0.125895 -0.008983  0.014724    2.69      0  \n",
       "2 -0.139097 -0.055353 -0.059752  378.66      0  \n",
       "3 -0.221929  0.062723  0.061458  123.50      0  \n",
       "4  0.502292  0.219422  0.215153   69.99      0  \n",
       "\n",
       "[5 rows x 31 columns]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Kolona 'Time' predstavlja broj sekundi proteklih izmedju tekuce transakcije i prve transakcije u skupu podataka i necemo ga koristiti prilikom obucavanje modela."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(labels=['Time'], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Kolona 'Amount' predstavlja iznose obavljenih transakcija. Kao sto se moze videti sa histograma, raspodela iznosa transakcija je veoma iskrivljena - velika vecina transakcija ima male iznose (manje od 100$), ali imamo i nezanemarljiv broj transakcija sa drasticno vecim iznosima. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<AxesSubplot:>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 7096 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "data['Amount'].hist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True     227422\n",
       "False     57385\n",
       "Name: Amount, dtype: int64"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(data['Amount'] < 100).value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Logaritamska transformacija prirodno smanjuje raspon vrednosti i velike razlike izmedju pojedinacnih vrednosti pretvara u manje, time transformisuci raspodelu vrednosti blize normalnoj raspodeli (koja je prikladna za vecinu algoritama ucenja). \n",
    "\n",
    "Primenju logaritamsku transformaciju na vrednosti kolone 'Amount' i iscrtavamo histogram tako dobijenih vrednosti. Da bi se izbegle 0 prilikom racunanja logaritma dodajemo malu vrednost $\\epsilon$ iznosima transakcija."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<AxesSubplot:>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 6744 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "eps = 0.001\n",
    "data['Amount'].apply(lambda x : np.log(x + eps)).hist()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dodajemo novu kolonu 'LogAmount' a izbacujemo staru kolonu 'Amount'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['LogAmount'] = data['Amount'].apply(lambda x : np.log(x + eps))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(labels=['Amount'], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Kolone 'V1', ..., 'V28' predstavljaju informacije o transakcijama dobijene redukcijom dimenzionalnosti originalnog skupa kako bi se zastitila privatnost podataka korisnika. Njih cemo zajedno sa kolonom 'LogAmount' koristiti kao atribute nad kojima ce model biti obucavan.\n",
    "\n",
    "Kolona 'Class' ukazuje da li je transakcija regularna (vrednost 0) ili ne (vrednost 1) i nju cemo koristiti kao ciljnu promenljivu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = data.drop(labels=['Class'], axis=1)\n",
    "y = data['Class']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Graficki prikaz udela regularnih i neregularnih transakcija u skupu podataka:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 6344 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist(y)\n",
    "plt.xticks([0, 1], labels=['regular', 'fraud'])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Odavde mozemo uvideti da je rec o visoko nebalansiranom skupu podataka."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podatke delimo na skup za treniranje i skup za testiranje u razmeri 2:1, pritom vodeci racuna o stratifikaciji (parametar <code>stratify</code>). Vrednost parametra <code>random_state</code> postavljamo na 7."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train, x_test, y_train, y_test = model_selection.train_test_split(x, y, test_size=0.33, stratify=y, random_state=7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(190820, 29)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_train.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(93987, 29)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x_test.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Provera stratifikovanosti podele:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    190490\n",
       "1       330\n",
       "Name: Class, dtype: int64"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_train.value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    93825\n",
       "1      162\n",
       "Name: Class, dtype: int64"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y_test.value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vrsimo standardizaciju atributa."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "scaler = preprocessing.StandardScaler()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "StandardScaler()"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scaler.fit(x_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train = scaler.transform(x_train)\n",
    "x_test = scaler.transform(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 1 - logisticka regresija"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Prvo cemo pokusati sa modelom proste logisticke regresije, bez ublazavanja nebalansiranosti klasa. On ce nam koristiti kao referentni model (eng. baseline) sa kojim cemo kasnije porediti ostale modele."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "model1 = linear_model.LogisticRegression()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression()"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model1.fit(x_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo predikcije modela na test skupu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_predicted = model1.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo matricu konfuzije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[93813,    12],\n",
       "       [   67,    95]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.confusion_matrix(y_test, y_test_predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Primetimo da je broj pogresno klasifikovanih neregularnih transakcija znacajan u odnosu na to koliko takvih instanci uopste imamo u skupu za testiranje."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ispisujemo klasifikacioni izvestaj."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0       1.00      1.00      1.00     93825\n",
      "           1       0.89      0.59      0.71       162\n",
      "\n",
      "    accuracy                           1.00     93987\n",
      "   macro avg       0.94      0.79      0.85     93987\n",
      "weighted avg       1.00      1.00      1.00     93987\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(metrics.classification_report(y_test, y_test_predicted))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Primetimo da su metrike za klasu 0 dobre, ali za klasu 1 postoji prostor za poboljsanje. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Za izracunavanje AUC mere i iscrtavanje ROC krive mogu se koristiti funkcije <code>roc_auc_score()</code> i <code>plot_roc_curve()</code> iz modula <code>metrics</code>. \n",
    "\n",
    "**NAPOMENA**: funkcija <code>roc_auc_score()</code> ocekuje skorove koje model pridruzuje instancama, ne labele prediktovanih klasa!\n",
    "\n",
    "**NAPOMENA**: počev od sklearn verzije 1.2 funkcija <code>plot_roc_curve()</code> je zamenjena funkcijom <code>RocCurveDisplay.from_estimator()</code>!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_probabilities_predicted = model1.predict_proba(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.974110522281763"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Za iscrtvavanje ROC krive moze se koristiti funkcija <code>plot_roc_curve()</code> iz modula <code>metrics</code>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<sklearn.metrics._plot.roc_curve.RocCurveDisplay at 0x7fca3d70f070>"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 15500 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "metrics.plot_roc_curve(model1, x_test, y_test)       "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Na osnovu oblika ROC krive i vrednosti AUC mere rekli bismo da je model koji smo naucili prilicno dobar. Medjutim, dobijene ocene modela su pristrasne zato sto je i sam test skup nebalansiran. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class='alert alert-info'>\n",
    "DOMACI:\n",
    "    \n",
    "Izdvojiti balansiran skup za testiranje i na njemu ponovo evaluirati model.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 2 - logisticka regresija sa eliminacijom instanci vecinske klase"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podrska za nasumicnu eliminacija instanci vecinske klase dostupna je na nivou <code>imblearn</code> biblioteke kroz klasu <code>RandomUnderSampler</code> modula <code>under_sampling</code>. Parametrom <code>sampling_strategy</code> utice se na zeljeni odnos broja instanci manjinske i vecinske klase nakon eliminacije."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vrsimo eliminaciju instanci vecinske klase u razmeri 5:1. Vrednost parametra <code>random_state</code> postavljamo na 7. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "rus = imblearn.under_sampling.RandomUnderSampler(sampling_strategy=1/5, random_state=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NAPOMENA**: Undersampling bi trebalo vrsiti i nad trening i nad test skupom nebili smanjili uticaj nebalansiranosti klasa kako na proces ucenja modela, tako i na ocenu modela pri evaluaciji. Medjutim, mi cemo to uraditi samo na trening skupu kako bi ocena modela bila uporediva sa ocenom baseline modela (izracunata na istom skupu)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train_resampled, y_train_resampled = rus.fit_resample(x_train, y_train) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Brojnost klasa nakon eliminacije instanci vecinske klase:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1650,  330])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.bincount(y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Konstruisemo model proste logisticke regresije i vrsimo obucavanje na transformisanom skupu za treniranje."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "model2 = linear_model.LogisticRegression()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression()"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model2.fit(x_train_resampled, y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo predikcije modela na polaznom test skupu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_predicted = model2.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo matricu konfuzije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[93208,   617],\n",
       "       [   19,   143]])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.confusion_matrix(y_test, y_test_predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mozemo primetiti da se broj FN instanci smanjio, ali da se broj FP instanci povecao. To nam je u redu zato sto je od veceg prakticnog znacaja da se neregularne transakcije ne klasifikuju kao regularne, nego obrnuto. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo AUC meru."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_probabilities_predicted = model2.predict_proba(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9739308470918738"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vrednost AUC mere se nije znacajno promenila. Razlog za to moze biti da je dobijeni model veoma slican baseline modelu, a da je samo granica izmedju klasa pomerena. To ima direktne veze sa pomeranjem praga klasifikacije koji ne utice na vrednost AUC mere, ali utice na matricu konfuzije sto smo i primetili. Drugi razlog moze biti to sto je skup koji koristimo za testiranje i dalje znacajno nebalansiran, pa su ocene oba modela podjednako pristrasne u odnosu na klasu 0."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 3 - logisticka regresija sa multipliciranjem instanci manjinske klase"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podrska za nasumicno ponavljanje instanci manjinske klase dostupno je na nivou <code>imblearn</code> biblioteke kroz klasu <code>RandomOverSampler</code> modula <code>over_sampling</code>. Parametrom <code>sampling_strategy</code> utice se na zeljeni odnos broja instanci manjinske i vecinske klase nakon multipliciranja."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vrsimo multipliciranje instanci manjinske klase u razmeri 1:5. Vrednost parametra <code>random_state</code> postavljamo na 7. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "ros = imblearn.over_sampling.RandomOverSampler(sampling_strategy=1/5, random_state=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NAPOMENA**: Oversampling bi trebalo vrsiti i nad trening i nad test skupom nebili smanjili uticaj nebalansiranosti klasa kako na proces ucenja modela, tako i na ocenu modela pri evaluaciji. Medjutim, mi cemo to uraditi samo na trening skupu kako bi ocena modela bila uporediva sa ocenom baseline modela (izracunata na istom skupu)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train_resampled, y_train_resampled = ros.fit_resample(x_train, y_train) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Brojnost klasa nakon multipliciranja instanci manjinske klase:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([190490,  38098])"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.bincount(y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Konstruisemo model proste logisticke regresije i vrsimo obucavanje na transformisanom skupu za treniranje."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "model3 = linear_model.LogisticRegression()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression()"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model3.fit(x_train_resampled, y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo predikcije modela na polaznom test skupu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_predicted = model3.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo matricu konfuzije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[93332,   493],\n",
       "       [   17,   145]])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.confusion_matrix(y_test, y_test_predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo AUC meru."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_probabilities_predicted = model3.predict_proba(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9723914366449228"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Primetimo da je matrica konfuzije slicna kao kod modela sa undersampling-om, a vrednost AUC mere slicna kao kod oba prethodna modela."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 4 - logisticka regresija sa generisanjem novih instanci manjinske klase SMOTE algoritmom"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**SMOTE** (Syntetic Minority Over-sampling Technique) je jedan od najpoznatijih algoritama za generisanje sintetickih instanci. Za svaku instancu manjinske klase posmatra se okolina *k* najblizih suseda (*k* - parametar algoritma), takodje pripadnika manjinske klase. Duz pravca koji spajaju uocenu instancu i njene susede (sve ili neke od njih, u zavisnosti od potrebe) interpoliraju se nove instance. S obzirom da nema eksplicitnog ponavljanja instanci, ublazena je mogucnost preprilagodjavanja. Mana ove tehnike je u tome moze uvesti dodatni sum u skup podataka ukoliko se u okolinama koje se posmatraju nalaze i instance vecinske klase."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"assets/SMOTE.png\" width=350>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Na nivou <code>imblearn</code> biblioteke, ovaj algoritam je dostupan kroz klasu <code>SMOTE</code> modula <code>over_sampling</code>. Parametrom <code>k</code> se zadaje broj najblizih suseda koji se posmatraju, dok se parametrom <code>sampling_strategy</code> utice se na zeljeni odnos broja instanci manjinske i vecinske klase nakon generisanja novih instanci."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vrsimo generisanje novih instanci manjinske klase u razmeri 1:5 posmatrajuci 5 najblizih suseda. Vrednost parametra <code>random_state</code> postavljamo na 7."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "smote = imblearn.over_sampling.SMOTE(k_neighbors=5, sampling_strategy=1/5, random_state=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NAPOMENA**: Oversampling bi trebalo vrsiti i nad trening i nad test skupom nebili smanjili uticaj nebalansiranosti klasa kako na proces ucenja modela, tako i na ocenu modela pri evaluaciji. Medjutim, mi cemo to uraditi samo na trening skupu kako bi ocena modela bila uporediva sa ocenom baseline modela (izracunata na istom skupu)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train_resampled, y_train_resampled = smote.fit_resample(x_train, y_train) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Brojnost klasa nakon generisanja novih instanci manjinske klase:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([190490,  38098])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.bincount(y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Konstruisemo model proste logisticke regresije i vrsimo obucavanje na transformisanom skupu za treniranje."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "model4 = linear_model.LogisticRegression()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression()"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model4.fit(x_train_resampled, y_train_resampled)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo predikcije modela na polaznom test skupu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_predicted = model4.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo matricu konfuzije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[93282,   543],\n",
       "       [   17,   145]])"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.confusion_matrix(y_test, y_test_predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo AUC meru."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_probabilities_predicted = model4.predict_proba(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9711298286473701"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Primetimo da je matrica konfuzije slicna kao kod modela sa random undersampling-om i oversampling-om, a vrednost AUC mere slicna kao kod sva tri prethodna modela."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class='alert alert-info'>\n",
    "DOMACI:\n",
    "    \n",
    "Eksperimentisati sa drugim algoritmima za generisanje sintetickih instanci.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model 5 - logisticka regresija sa tezinama"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Slicno kao kod modela linearne regresije sa tezinama, i ovde se umesto sume gresaka na pojedinacnim instancama minimizuje se njihova tezinska suma\n",
    "\n",
    "$$ -\\sum_{i=1}^{N} \\omega_i (y_i log f_{\\beta}(x_i) + (1-y_i)log(1-f_{\\beta}(x_i)))$$\n",
    "\n",
    "gde je $f_{\\beta}(x_i) = \\sigma(\\beta x_i)$. Koeficijenti $\\omega_i$ predstavljaju tezine koje se pridruzuju instancama. Za vrednosti $\\omega_i = 1$ dobija se osnovni oblik problema logisticke regresije."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Kako bi ukupna greska bila podjednako rasporedjena na obe klase (na svaku od klasa, u opstem slucaju), instancama treba pridruziti tezine koje su obrnuto proporcionalne kardinalnosti klasa. \n",
    "\n",
    "Prilikom poziva konstruktorske funkcije modela logisticke regresije tezine instanci po klasama zadaju se parametrom <code>class_weight</code>. Postavljanjem na vrednost <code>'balanced'</code> klasama, odnosno njihovim instancama, bice pridruzene tezine po sledecoj formuli:\n",
    "\n",
    "$$\\omega_i = \\frac{velicinaTreningSkupa}{brojKlasa \\cdot brojInstanciKlaseC_i}$$\n",
    "\n",
    "Pridruzivanje razlicitih tezina instancama u okviru iste klase je takodje moguce zadavanjem parametra <code>sample_weight</code> prilikom poziva metoda <code>fit()</code>."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Konstruisemo model logisticke regresije i vrsimo obucavanje na transformisanom skupu za treniranje. Vrednost parametra <code>class_weigth</code> postavljamo na <code>'balanced'</code>."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "model5 = linear_model.LogisticRegression(class_weight='balanced')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression(class_weight='balanced')"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model5.fit(x_train, y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo predikcije modela na polaznom test skupu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_predicted = model5.predict(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo matricu konfuzije."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[91505,  2320],\n",
       "       [   14,   148]])"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.confusion_matrix(y_test, y_test_predicted)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Izracunavamo AUC meru."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "y_test_probabilities_predicted = model5.predict_proba(x_test)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9732266532453049"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Primetimo da je matrica konfuzije malo bolja u odnosu na prethodne modele po pitanju pogresno klasifikovanih neregularnih transakcija. Vrednost AUC mere je slicna kao kod sva cetiri prethodna modela."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tezine klasa kao metaparametar modela"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tezine se mogu pridruzivati klasama (tj. njihovim instancama) i proizvoljno zadavanjem odgovarajuceg recnika za vrednost <code>class_weight</code> parametra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "model5_2 = linear_model.LogisticRegression(class_weight={0 : 0.05, 1 : 200})  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "U opstem slucaju se tezine biraju nakon veceg broja eksperimenata sa razlicitim vrednostima. Sledeci primer ilustruje proceduru. Konstruisacemo modele za razlicite vrednosti tezina (ukupno 4 kombinacije) i odabrati onu kombinaciju tezina za koju se dobija najbolji model u smislu neke izabrane metrike (kod nas AUC mera)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "class_weights = [{0 : 0.01, 1 : 290}, {0 : 0.05, 1 : 350}, {0 : 0.05, 1 : 290}, {0 : 0.05, 1 : 200}]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "roc_auc_scores = []\n",
    "\n",
    "for class_weight in class_weights:\n",
    "    model = linear_model.LogisticRegression(class_weight=class_weight)\n",
    "    model.fit(x_train, y_train)\n",
    "    \n",
    "    y_test_probabilities_predicted = model.predict_proba(x_test)\n",
    "   \n",
    "    roc_auc_score = metrics.roc_auc_score(y_test, y_test_probabilities_predicted[:, 1])\n",
    "    roc_auc_scores.append(roc_auc_score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Vizuelizacija vrednosti AUC mere dobijenih modela za razlicite vrednosti tezina klasa:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 19400 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(np.arange(0, len(class_weights)), roc_auc_scores)\n",
    "plt.xticks(np.arange(0, len(class_weights)), labels=class_weights)\n",
    "plt.title('AUC vrednosti za razlicite tezine')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Maksimalna vrednost AUC mere koja se dobija je:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9741698328579933"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.max(roc_auc_scores)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Kombinaciju vrednosti tezina klasa za koju se dobija maksimalna vrednost AUC mere je:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{0: 0.05, 1: 200}"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "optimal_class_weight = class_weights[np.argmax(roc_auc_scores)]\n",
    "optimal_class_weight"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**NAPOMENA**: Evaluaciju modela dobijenih za razlicite vrednosti parametara kojima se konfigurisu modeli (tzv. metaparametri ili hiperparametri) nije ispravno vrsiti na skupu za testiranje, vec na zasebnom (trecem) skupu. O tome ce biti vise reci na nekom od narednih casova kada se budemo bavili tehnikama evaluacije i izbora modela. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**VAZNO**: Ovaj pristup se moze primeniti i kod drugih klasifikacionih modela. Svi oni imaju <code>class_weight</code> parametar."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
