2023-10-25 12:12:35 +02:00
{
"cells": [
{
"cell_type": "markdown",
"id": "c75106d7-67aa-4c6c-afd7-d4e135663c60",
"metadata": {},
"source": [
"# Regresja logistyczna"
]
},
{
"cell_type": "markdown",
"id": "1d7d4df5-6af4-4ad8-b684-d2cdea71b65e",
"metadata": {},
"source": [
"Na dzisiejszych zajęciach zajmiemy się problemem klasyfikacji danych. Wykorzystamy do tego regresję logistyczną."
]
},
{
"cell_type": "markdown",
"id": "9bce9f86-9a0c-49a8-b08b-15ddfe135256",
"metadata": {},
"source": [
"## Wprowadzenie\n",
"\n",
"Kiedy chcemy zdecydować jak zaklasyfikować jakieś obiekty o konkretnych cechach możemy np. określić 2 lub więcej klas obiektów i obiekty przypisać do tych klas. Natomiast jeśli algorytmy mają to zrobić za nas, przypiszą obiekt do jakiejś klasy na podstawie wyliczonego prawdopodobieństwa należenia do klasy. To znaczy, że jeśli mamy dwie klasy: A i B, to algorytm może np. wyliczyć, że obiekt można zaklasyfikować jako A z prawdopodobieństwem 0.8 a jako B z prawdopodobieństwem 0.2 to oczywiście algorytm przypisze obiektowi klasę A, dlatego że prawdopodobieństwo jest wyższe, choć nie jest równe 1 - nie mamy pewności, że obiekt rzeczywiście należy do klasy A."
]
},
{
"cell_type": "markdown",
"id": "d21f295f-dee6-419e-b8f3-f00fd8941448",
"metadata": {},
"source": [
"## Regresja logistyczna jako klasyfikator\n",
"\n",
"Wiemy już, że będzie nam potrzebne prawdopodobieństwo i wiemy jak ocenić poprawność działania naszego algorytmu. Ale skąd wziąć prawdopodobieństwo?\n",
"\n",
"Przypomnijmy sobie najpierw jak działała regresja liniowa, którą znamy z poprzednich zajęć. Na podstawie jakiejś cechy lub zestawu cech (zmiennych niezależnych) funkcja ta przybliżała nam wartość zmiennej zależnej od tych cech. Np. procentowy wynik na teście końcowym w kursie względem rozwiązanej liczby ćwiczeń, liczby wejść do kursu, czasu spędzonego w kursie, itp. Załóżmy, że zamiast konkretnego wyniku chcemy przypisać danemu uczniowi etykietę \"zda\" (wartość = 1) lub \"nie zda\" (wartość = 0). Co by było, gdybyśmy zastosowali tutaj regresję liniową? Mogłoby to na wykresie wyglądać mniej więcej tak:"
]
},
{
"cell_type": "code",
2023-10-25 13:12:40 +02:00
"execution_count": 1,
2023-10-25 12:12:35 +02:00
"id": "7db5ff10-5a9a-4036-89cb-d3f3413347d5",
"metadata": {},
2023-10-25 13:12:40 +02:00
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAC7gAAAaZCAYAAADcMkIqAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAgcwAAIHMBlDE8ggAAIABJREFUeF7snft3Xld557+SLNmyZTt2fI/jS0NCAwMEk5ZSaNWWS2lhBkoTQmkgYUrLwABlhjYNJWUVKKXzJ8xPXWsKckLCpaSULMqtFAiBkDRXciO+xff7VbZl65113pP35T3S2Wcf2ZL2V/bnXWvWzHB2JHk/+3k+z/e7Hx11NRqNhhJ9zp49q+PHj6u7u1sDAwOJfor82x47dkyjo6PNnyP7eabzMzIyouHhYc2aNUtz586dzm9d+F5O8Ui2CZKIx/jdJz/UrFUPP/ywent7de2116Y8otQrSU71KmV+7N27V0899ZQWL16sq6++Otm5JB751sOP8Ufw/vvvb+7Ly172smnvcYgHPC8rivCcehWCJTynv2qdDfgBz8vqBP2uFz/guVc8yI88Huhz9Ic7P9Dn9Ltu/S48h+fo87Cdb91fnT4tbd4sHT0a/gfs3Clt3Cg991z1ncWLXiS9973BNWfOnNHJ+fPVvW7dtHvLnT+UUzzgOTyH5+GyktLfxU/00oPoc694kB/47e5+CfxgHs6tv3LSHynzo5PnXSkH3E+fPt0c7M4+2WB5T09PtdCdoqfZwcgCkn36+/vV19c3Rd+p/Mtme5DtRfZZuHDhtH7vzm/mEo9kG/D8NyYexQiQH/l+7NixQ08//XTz//2KV7wi2S/lEI88Hi71KnU8nnzySe3atau5J4ODg8nKJ/HItx5+FI9g1lv99Kc/bf6PV111lVauXDmtZ5R4wPOyAwfPqVdl5wKe0191ngv4Ac/L6gT9rhc/4LlXPMiPPB7oc/SHMz/Q5/S7jv0uPIfn6POwVWrbX509mw+3nzgR/uE3bZKGhqT9+6u94Fe+Urrhhso1Jy+5RKeWLGmu4f48f1kffjv3g62kcfGv4Dk8h+czkOfMwyWdT4Tn6HP0ebhupr6vdemvbPXgNPOj029nwP35NwGnHHA/ceJE8+2mCPRqr2O6nhIPrwspl3hs375dzzzzTHNzNmzYoPnz50/XkSx8n9RAd4kHQM+PxRNPPKHdu3c3/98MuOdv7oDnPgbv0aNH9cADDzTP55VXXqlVq1ZNa92kXsHzsgMHz/NdIT+88gOee8WD/CjGA55juHeeCJf8gOfwvKzPS60H4Tk8LzuXLv4VPIfn8DxsSaXmh0t/5VKvUscDnlfw/OxZ9e3YIZ06FU6oxx/P39x+/Hi1D/y610m///vVa1av1on587k/79gleA7P4Tk8j12ywfN8h+A5+hx9Hq4W6A/yg/tz8iPWT7SeO+lzBtwZcG+fW5eG95//+Z913333af369cr+TOVHPvKRurk1KesAOkAH6AC9bjFxAjoD7gy4t86tC88x3DHcMdwx3GM8dalX8Jx65VivXPIDnpMfjvnBgHseFfwrL/+KC3SveJAfxXjAc3gOz9Hn6PPYDuTP4Xk5z7uOHFH/7t3q7eoKb2T2opNsuD17y3vV561vlX7zN6vXrF0rLVlCvztml+A5PIfn8DxGMxc/Eb+deuVYr1zyA56TH475gd+O317WYzjxnAF3BtzbZ9QB6Nlw+1/+5V8W8uYDH/jAtA65cwHChVRZ4QboAN0d6Ay4M+DeOqMOPM9+FgQ6Ah2BjuGO4R7bgfw5F+joj7KTAs/RH476w8UvQZ+TH475Ac/hOTwP974u/HDpr1JfELrEA57Dc3gerptO9er4tm3q3rpVs/v61NvbW/5D//CH0l13xU2Qd71LevnLw+uyAfr166VFi5prXOqVSzzw2/Hb8dvx22OF1qVepe530efoc/Q5+jxWL1vPU9crl34XfY4+d9fnDLgz4N4+o6kb3rLh9tYPN51D7i4ASR0PgF4s3wAdoLsDnQF3BtxbZ9SFHxjuGO4Y7hjuMQPJpV6lNrAw3DHcMdwx3GP1En2OPi87I/hXXvyA517xID+K8UCfo8/R5+jzWL+JPs93CJ6P4fmuXRp+8snm/zh79uzyAfdvf1v6l3+JHTHp/e+XrrwyvG7WrHy4fcGC9hp4Ds+d/ZLUfqJLfnB/zv059+dhtNFfkR9lpwN9jj5Hn6PPY+IJfozX5wy4M+DezpuUCTJ2uP13f/d3tXjxYm3M/pzd85/pGnJ3EYQp49FZTBHo+W4g0BEgCHQEeqzRhB8Y7hju4SxxyQ94Ds/hOTyH57EdgOfwHJ7XzRL8Ei6kOs8KA3HFzCE/vPKDC3SveJAf+O2dFdPFL+E+avwF+kX/QplduzS6bVvzLerZp3TA/Wtfk771rXj7fMst0ooV4XWzZ+fD7fPmFdaQH+hz9Dn6PF5g8hX0V/S76PNwtpAfXvmBPveKB/mBPkef+/Kj029nwJ2Gt31SUxlYY4fb3/Oe9+gf//Efmz/XH/3RH+n2229Xo9Fo/v+nY8gdw4QLqbLyzUBcvivkh1d+cIHuFQ/yA8Mdwx3DHcO97g5gYGG4+xom8Byew3N4XpdmXIDAc3gOz2P1IpXfPvbn4gKdeuV4YYvfjt9eVkNT91f47c9HZft2KRtwHx0ND7h/8YvSD34QQ6H0qU9JAwPhdXPn5sPtc+aMW4M+R5+jz9Hn8SKTr0jND+pVMVKp4wHPveJBfsBzeA7P4XndHcC/CvntDLjT8LbPRgrDfexw+1//9V9rw4YNuu6665o/1yc+8Qldfvnl+rM/+7NpG3KnwaLhLUMLhjuGO4Z7uOlKwQ/HeMAPBDoCHYFeV56mNnipV179Loa7VzzID3gOz+E5PK+7AxjuIcP9on/jK357Ur+9LIMZcKdedZ4Ll34Xvx2/3dHfRZ9L2rpV2ru3GZ7SAffRUSn769s//Wl105i9lf2zn61eM39+Ptze21u6zqVeudx/wHN4Ds/DJQW/nfxAn5MfMTcLnqM/HPWHS7+LPic/HPODN7iPiQoNb5qGd+xw+8c//nHddNNNevjhhwsD7jfeeKP+/d//fdqG3F0A4tJgkR95fgB0gO4OdC7QeUND64y68APDPU1/FTJw4Dk8d7wAcalXqfODC/Ri5UodD/RgMR7wHJ478gN9jj5Hn4evbemvyI+y0wHP4Tk8Z+Bnpgz8pNaDF7U+z/6S9ubN0oED7eMybsB9ZEQaGpIef7z6SC1ZIv31X1evueSSfLi9uzu4Dn2OPi87HPS7Xv0u+twrHuRHHo+LmuclhTN1fwXP4Tk8D7fFLvkBz+G5u9/OG9x5o0z7jE5nwxsabs9+mLIB9+x/n64hdxeATGc8qlwmGt58dwA6QHcHOgPuDLi3zqgLP7hA5wKdC3Qu0LlAj+0Ahrtjf4UexHDHcMdwr1e90R9u+iO1f8UFejFzUscDnsNzeA7P4XndHcC/6typi5bnZ87kw+2HDxcOTmHA/fhx9d55p/Tss9WHa9066cMfrl5z6aVSti7ygefwHJ7D81idaD1Hf8BzeM59VKxecH+e7xD9Ff5VWa4wD0d+lJ2L1P0Vb3AfE5XUAbnYAFI13J6FJjTgnj2bjiH3iy0esUaP/Mh3CKADdHegM+DOgEnrjCLQqVeO9cqlv4Ln5Idjfly0F+gBIYL+8LqQ4hfWvOJBfqDPO0unS3/loj9S5wc854KwrLVxyQ94Ds8d+YE+R5+jz8O3c9PKj1On8uH2Y8fG/UCtAffunTs158tfVveuXdVXii95ifSe91SvWbZMuvzy2NVk8zn9bnGb4Dk8h+fh0pFaD1KvvPQg+twrHuQHPHf2S+AHfrtjfzWterBCmaXODwbcxwQndUAuJqDHhtuz0FQNuGfPp3rI/WKKRx0HifwA6ADd1zBBoCPQnQUhhjuGuyM/uEDnAp0LdJMLdGPDBD2I4e7cX6HP0eeO/RWGex4V9Dn63Jkf6HP0uSM/0Ofoc/S5gT4/cSIfbh8eLv1hsgH3k489pt677lLP4cPq7u4O/9CvepV0/fXV144rV0qrVtW5mmyuQZ+jz537K/Q5+tyxv0Kfo88d+yt4Ds/hebj9dckP9Dn63JEfDLiPiQoCZHoM3rvvvlsf/ehH27v/8Y9
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Image\n",
"Image(filename='regLog1.png')"
]
2023-10-25 12:12:35 +02:00
},
{
"cell_type": "markdown",
"id": "7ab8d303-437d-4674-84a9-7856a7563688",
"metadata": {},
"source": [
"No cóż, widzimy, że tym razem regresja liniowa nie pomoże nam przy estymowaniu wartości przewidywanej, bo w zbiorze uczącym nie będziemy mieli innych wartości zmiennej zależnej niż 0 i 1. Co teraz?\n",
"\n",
"Może spróbujmy przybliżyć zbiór wartości zmiennej zależnej inną funkcją, np.:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 2,
2023-10-25 12:12:35 +02:00
"id": "8121eab5-bf59-4994-bef8-08777c9a5728",
"metadata": {},
2023-10-25 13:20:33 +02:00
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAC7gAAAaZCAYAAADcMkIqAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAfhwAAH4cBG/40IgAAIABJREFUeF7svVuPrWlVt3+vVTVrM2uu1TS9A8HCkj2rZbNCe2In7QfwWA/9Bp74BfTYtNEQNM0BokZNPNBoEENiop1uteNCBGTbgh1aW2jpBlYXCA0U/WY843/3O4q3cM2i5nqu+8/v9yQkZVtzzlVj3JvrGeN67nnppZdeeqnNcH35y19un//859t9993X3vjGN87wiac/4pvf/GY7OTlpy+WybW9vz/75/QO//e1vt+985zttb2+v7ezszPrviFR/4xvfmD5ztVq1S5cuzfr58XfH3x9/d/z9qte3vvWt9t3vfhcZA9///vdbzAVqDNScx1iMf8/BwUHb2tqadTh873vfa//zP/8zrQWxJsx9feYzn2lf/epX25vf/OZ29913z/rxsQ7GGLh8+fK0DpBXHwPx74h/z5xXzMGYi9QYGGVPeuqpp9ozzzzTXve617Wf/MmfnDMFre5JsQ7MPQb6nrRYLNr+/v6sf3v9MHpOklwSOfiXf/mXKRzXr19vkQvqIudk35NiL4y5MPdFckn9W2lO/Od//uf24osvtre//e2z7480l/Q80JwY8Y//UfcKJJfUuaDMiX1P2t3dbfE/6lLmxOPj4/av//qv071q7M3Upc6JJJfUnCtzIs0lPQ/KnEhzSZ0LypwYtbMYh3HPTN6vKXPiV77ylfbkk0+2V77yle2tb30rtTVPfRWynkhyIs0lNekkI9CcSHLJWbULqp5IjgGaSz73uc+1WBOjvxx9ZuqiOVG5rzZK/erpp59u8b/Xvva17ad+6qdmH4qj1K9ITlTmkjrgaE4k9yR6DPQ80JxI9zk//vGPT/cI165da3fcccfs62H/QJIT7V9lFpQ5caT6lTInklxSFz9lTqT7aj0PNCOQ9av426OOHevSu971LrTPejs48dJcgvunPvWp9txzz00y4c/93M/NCjgBFi+88ML0mdEoJ+XqKATGYIo4zC0xxYISC3tcIfXO3ZiIARxwExLjlStXZh0DI31YjMUYkxH/ueXqOgZi/FEPe8QciLkQV8zHuQWWkPnipi+uq1evzv6wx6OPPjp99qte9apJcp/zir87/v64Qiyf++GCChb9gZtolM79wE8dA3PfdNc9Kf5uUq6+cePGtC/EPAjImfOK/aA/cEPsSfF3x5oYD3vF309d0agP0I0r9sa5RX+SS55//vn2yU9+cvrb77///nbXXXchaaA5MfIf44Dak0guqQknOTH2hH/6p3+a/jk//dM/PfsDPzSX9DzQnBhcEDffwSZzP4QXn0tySZ0LypzY9yRiDNQcKHPif/zHf7R///d/n8Lxsz/7sxinKnMizSV1LihzIsklNQfKnEhzSc+DOifevHlzCgVdu1DmxBA64+CguB566CHknjk+lK4nkpxIc0lPOl1PpDmR5JI68ch6Is2JNJf8/d///dRfvOeee9rb3vY2bD0kOVG9rzZK/SoObIl6avRY3/3ud886FkeqX5GcqMwldcCRnEhzCd1X63mgOZHkkpiH//iP/ziFIg5RIx746XkgOdH+VWZBmRNHqV+pcyLJJXVvVuZEsq9Wc6DMiV//+tdbPHwWVxySce+99856r3K761ezC+7RrH7wwQdnDWJdzGnBvUtEFtwtuBOCe13MLbjzgnucNPKWt7xl1vWQ3tD7H0sX4vpNf/x7LLj/zyRWz31KZy38EA85kIWfOunpQhzJJfHgYzwAGVec8DD3N1r0PNCcWAs/xEMOFtzb9KBPPPAT19HRUTs8PJx1bx5FcKc5sYtLxMOwNJfUAdcFd0VOJMWls6QVRU6ME0biG37ieuCBB2Z/IPusBqEaJ9JcUueCMieS4lLNgTIn0lzS86DOiaM0CJU58bOf/Wx79tlnpyFpwf37yMOwdP3qdjcI1735pTmR5JKz7hWIAzNoTqS55PHHH58eio/6YdQRqYvkRHVxqeec5sSPfvSj0wFecWhP3DfPeY1UvyI5ke5zjlK/IjnRgnvOfJoTyT5n9DafeOKJKQ7RT4m+CnWRnGjBPbOuzIk0l/R5p86JJJfUtU+ZE0n/quZAmRO/9rWvtU984hNTOMJDpL757HZxogX3mUnLgMWf4P5rv/ZrUwHsb//2b9sv/dIvzd4cIEUyA1ZOeFokUxaX+pJLF+JIwLpdG/qPsp31E9wtuOue4E5yiQX3MQo/JJfUdYtsEKqLS6M0CJXFpToXlDlxlAahMifS4lKfC8oNQlpcquuRMieSXFJzQDcISU4cpX6lzomjNAiVOZEUl9wgHENc6nmg64k0J5JcUucCKZLRnEhziQX3Nn07uPI3I49Sv1IWl+p6SHKisrhUc0ByIs0l9MFRrl+16VuJLbi36ZvCgw/jIr6t3PWrnI0kJ45Sv1LnRJJLLLhnBMi+mutXGQEL7j+KMXfGa+KEzhCZfIL7Cy2g3ye4Mye4h9z+x3/8xy+P0Dhp433ve9+skrsbhC7EKYtLffJZcH9hCgX9Nd8W3L/biBOXKibRhTiyQWjBPUcC3SAkuaTOBbIQpy4ujdIgVBaX6lxQ5kQL7m2qFcS6THEiLS65QXj6Xln5GwhpTiS5pO4Jypw4SoNQnRNHaRAqcyIpLrlBmBGgH7zreVDnRLJ+VeeCBfcXp3AQ30BIikuj1K/UxaVR6lcW3DMTJCdacM8ckJxIcwndV3P9yoJ7HwMW3Mfoc5KcOEr9Sp0TSS6p9wrKnGjBPUcC2ee04F5n4wV+tuCewSMLceqAVeX2vb29SSiLa27JnRTJDFg5D32C+7emOKxWq+mhI+Ky4M6JSzXfFtwtuJNcQotLfS6on4BFcskoDUJ1cWmUBqGyuFTnggX37098GpxKXWQhjm4QWnDPUacsLtV5p8yJFtxP1zAXi8V0Etmc1yj1K3VOHKVBqMyJpLhU57yySGbBPUcCzYkkl9S5oMyJ9IN3pLg0Sv1KXVwapX6lLC7VuUByojKX1ByQnEjXryy48/Urn+CeOVD3r/qapMyJo9Sv1DmR5JK6NytzItlXc/0qI2DBfUPdCwvup5tDPsF93hPcq9wep0v84i/+YnvyySfbY489NiVmTsmdFMkMWDkPLbhbcCcBiy781G3dgrsFd7JBaME9ZyNd+CG5ZJQGobq4NEqDUFlcqnPBgrsFd5/gzp+Uqiwu1fVImRMtuJ+uYVpwvzEF5OjoqB0eHm6oUr7e29D1q1EahMqcSIpLbhBmBCy4ZxwsuGcclDmRrl9ZcPc3I49Sv1IWlyobkJxowT0zQXIi3ee04M5ziQX3zIEF9zH6nCQn2r/KMeD6VcZBmRNJ/8r1q4yABff16u23/C0L7hkiskGoClhVbr/zzjvbz//8z7c///M/n/Lxy7/8y+33f//3p5/nktxJkcyANQZgKYtLfbPwCe4+wZ1uEJINqQpNdCGO5BIL7mMUfkguqXOBFMksuGcmaE5UFpfqXFDmRPKr+2oOyEIc3SCkxaWeB2VOpL9Zps4FZU4kuaTmgBbJSE6kuaTnQZ0TSXGpzgVlTiTFJTcIMwI0l/Q8qHMiySVn3StEL+nq1au37Itu8hdoTqS5hBSXRqlfqZ/M2fNAc6KyuFTnAsmJFtwzEyQn0lxC99Vcv2rNgnuOAlX/6gcZV5kTaS7puVDnRJJL6nxQ5kSyr+b6VUbAgvuGKjAW3DOQZCFOEbB+UG5/3/ve1/7mb/6mPfLII1M+4gT3P/mTP2m/8zu/M/3fc0jubhD6pAllcalvKRbcLbjTDUIL7jyXWHDPHNCFH5JLRmkQqotLozQIlcWlOheUOdGCe2t0g9CCe85GkhNpcamuR2T9iuZEC+6n7xV8grtPcN/Z2Wn7+/sb6hKc/22UOZEUl9wgzAjQ9aueB3VOJLmkzgVlTqTrVxbc3VcbpX6lLC7V9ZAUySy4ZyZITqS5xII7X7+y4J45UPSvzrqjV+ZEC+45InyCe8ZBmRMtuOcYIPucFtzPX3M+8xUW3E83h7a3t9vBwcGGorve26g
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Image\n",
"Image(filename='regLog2.png')"
]
2023-10-25 12:12:35 +02:00
},
{
"cell_type": "markdown",
"id": "900d42f6-1deb-4eac-a57a-65b0fffe0a8a",
"metadata": {},
"source": [
"Ta funkcja już lepiej pasuje :)\n",
"\n",
"I o takiej funkcji mówimy, że jest funkcją **regresji logistycznej**. Jest to **funkcja sigmoidalna**. Dla funkcji logistycznej jednej zmiennej wzór w ogólności jest następujący:\n",
"\n",
"$$ f\\left ( x \\right )=\\frac{1}{1+e^{-x}}$$"
]
},
{
"cell_type": "markdown",
"id": "60c4cb42-d15c-49ea-8d55-aabf17cb5cde",
"metadata": {},
"source": [
"Natomiast my musimy dopasować tę funkcję do naszego zbioru danych i w tym celu szukamy pewnej funkcji liniowej, która jest argumentem funkcji $e^{-x}$. Na przykład jeśli mamy regresję logistyczną dla jednej zmiennej to szukamy funkcji liniowej $g(x)=ax+b$:\n",
"\n",
"$$ h\\left ( x \\right )=\\frac{1}{1+e^{-\\left ( ax + b \\right )}}$$"
]
},
{
"cell_type": "markdown",
"id": "8e8f5491-d94e-4a08-86be-33f699768eca",
"metadata": {},
"source": [
"Do znalezienia wartości $a$ i $b$ można wykorzystać np. algorytm gradientu prostego, o którym wspomniano na poprzednich zajęciach. Ale na potrzeby tych zajęć nie będziemy się tym zajmować. Nas interesuje wynik, więc skorzystamy znowu z gotowych bibliotek języka Python, które pozwolą nam wyznaczyć odpowiednią funkcję regresji logistycznej. Dla konkretnego wiersza w naszych danych wyznaczamy wartość takiej funkcji regresji logistycznej, którą potraktujemy jako prawdopodobieństwo należenia do pewnej klasy. I tutaj należy podjąć decyzję, od jakiej wartości prawdopodobieństwa będziemy mówić, że dany obiekt (u nas: _uczeń_) należy do danej klasy lub nie należy (inaczej mówiąc należy do klasy przeciwnej). Na przykład: _jeśli prawdopodobieństwo jest większe niż 0.5, to danemu uczniowi przypisujemy klasę **zda** a w przeciwnym wypadku klasę **nie zda**_."
]
},
{
"cell_type": "markdown",
"id": "bfb8aa0a-7ab7-4e01-a3d7-134f9b1c3f32",
"metadata": {},
"source": [
"Zatem przejdźmy do kodu. Na początek importujemy potrzebne biblioteki:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 3,
2023-10-25 12:12:35 +02:00
"id": "d43ede69-e85f-43c1-8bcb-dfa073585a2b",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
2023-10-25 13:12:40 +02:00
"import numpy as np\n",
"import math\n",
2023-10-25 12:12:35 +02:00
" \n",
"from sklearn.linear_model import LogisticRegression"
]
},
{
"cell_type": "markdown",
"id": "98151afc-2d56-4055-a978-265f0c148cae",
"metadata": {},
"source": [
"Wczytajmy dane i podejrzyjmy nasz zbiór danych:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 4,
2023-10-25 12:12:35 +02:00
"id": "9e006cb5-50d5-4b9d-996c-cf77a93fe88a",
"metadata": {},
2023-10-25 13:12:40 +02:00
"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>cwiczenia</th>\n",
" <th>czas_min</th>\n",
" <th>wejscia</th>\n",
" <th>nieodwiedzone</th>\n",
" <th>czas_do_testu_godziny</th>\n",
" <th>czy_zda</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2</td>\n",
" <td>5</td>\n",
" <td>1</td>\n",
" <td>4</td>\n",
" <td>5</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>4</td>\n",
" <td>12</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>6</td>\n",
" <td>25</td>\n",
" <td>3</td>\n",
" <td>3</td>\n",
" <td>36</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10</td>\n",
" <td>29</td>\n",
" <td>2</td>\n",
" <td>0</td>\n",
" <td>22</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>12</td>\n",
" <td>42</td>\n",
" <td>7</td>\n",
" <td>1</td>\n",
" <td>37</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>14</td>\n",
" <td>48</td>\n",
" <td>3</td>\n",
" <td>1</td>\n",
" <td>33</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>16</td>\n",
" <td>36</td>\n",
" <td>7</td>\n",
" <td>1</td>\n",
" <td>47</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>22</td>\n",
" <td>48</td>\n",
" <td>4</td>\n",
" <td>0</td>\n",
" <td>45</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>24</td>\n",
" <td>55</td>\n",
" <td>5</td>\n",
" <td>0</td>\n",
" <td>39</td>\n",
" <td>1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cwiczenia czas_min wejscia nieodwiedzone czas_do_testu_godziny czy_zda\n",
"0 2 5 1 4 5 0\n",
"1 4 12 1 1 20 0\n",
"2 6 25 3 3 36 0\n",
"3 10 29 2 0 22 0\n",
"4 12 42 7 1 37 1\n",
"5 14 48 3 1 33 1\n",
"6 16 36 7 1 47 1\n",
"7 22 48 4 0 45 1\n",
"8 24 55 5 0 39 1"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 4,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = pd.read_csv('data.csv', sep=';')\n",
"data"
]
},
{
"cell_type": "markdown",
"id": "02d66c97-824a-43ce-88ad-df2baaff8161",
"metadata": {},
"source": [
"Teraz wykorzystajmy regresję logistyczną:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 5,
2023-10-25 13:12:40 +02:00
"id": "f6b74ad8-6cfb-43b0-b319-4ba4d419adce",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([3.73769080e-04, 2.21604437e-03, 1.30204590e-02, 3.17614514e-01,\n",
" 7.34374026e-01, 9.42600926e-01, 9.89852217e-01, 9.99951084e-01,\n",
" 9.99991764e-01])"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 5,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = LogisticRegression()\n",
" \n",
"X = data[['cwiczenia']]\n",
"y = data['czy_zda']\n",
"\n",
"model.fit(X, y)\n",
"y_pred = model.predict_proba(X)[:,1]\n",
"y_pred"
]
},
{
"cell_type": "markdown",
"id": "24a3f8c8-0a3f-4897-848d-22085e1310ff",
"metadata": {},
"source": [
"Dla każdej wartości w kolumnie `cwiczenia` otrzymaliśmy wartość prawdopodobieństwa przynależenia do klasy `zda` (wartość kolumny `czy_zda` wynosi `1`)."
]
},
{
"cell_type": "markdown",
"id": "899d9b61-7f7e-4cda-a8e0-face34aeedc1",
"metadata": {},
"source": [
"A czy tutaj na pewno zastosowano wzór na regresję logistyczną? Sprawdźmy to sami:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 6,
2023-10-25 13:12:40 +02:00
"id": "c6fed6bf-2b23-4ca7-9340-282bd77da9e7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
2023-10-25 13:20:33 +02:00
"a = 0.89084278; b = -9.67318411\n",
2023-10-25 13:12:40 +02:00
"0.00037377\n",
"0.00037377\n"
]
}
],
"source": [
"a = model.coef_[0][0]\n",
"b = model.intercept_[0]\n",
"\n",
"x = data.loc[0, ['cwiczenia']]\n",
"\n",
2023-10-25 13:20:33 +02:00
"print(f'a = {round(a, 8)}; b = {round(b, 8)}')\n",
2023-10-25 13:12:40 +02:00
"\n",
"print(round(1/(1 + math.exp(-(a*x + b))), 8))\n",
"\n",
"print(round(y_pred[0], 8))"
]
},
{
"cell_type": "markdown",
"id": "a752a769-6576-484f-8457-0fecc2a3767f",
"metadata": {},
"source": [
"Wyniki się zgadzają :)\n",
"\n",
"No dobrze, to teraz podejmijmy decyzję - czy uczeń zda?"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 7,
2023-10-25 13:12:40 +02:00
"id": "ad7e06cb-a996-4734-8225-60072210a631",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0, 0, 0, 0, 1, 1, 1, 1, 1])"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 7,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def classify(prob, threshold):\n",
" return np.where(prob > threshold, 1, 0)\n",
"\n",
"classify(y_pred, 0.5)"
]
},
{
"cell_type": "markdown",
"id": "cab5885d-b90f-4cb4-886b-1f7cc95182d2",
"metadata": {},
"source": [
"Pisząc prostą **funkcję decyzyjną** otrzymujemy tablicę z wartościami `0` (`nie zda`) i `1` (`zda`) dla kolejnych wartości kolumny `cwiczenia` oraz odpowiednich wartości prawdopodobieństwa wyliczonych wcześniej."
]
},
{
"cell_type": "markdown",
"id": "9663aab1-88fb-4f8a-bcdb-f9d150221d3f",
"metadata": {},
"source": [
"Zatem mamy model, na którym możemy testować dopasowanie do odpowiedniej klasy na dowolnej liczby rozwiązanych ćwiczeń. Na przykład sprawdźmy, czy uczeń, który rozwiązał 15 ćwiczeń zda test:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 8,
2023-10-25 13:12:40 +02:00
"id": "39f74d57-3672-404e-b657-00ff78cbd0d8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.9756235096862668\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 8,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"value_to_predict = 15\n",
"value_predicted = model.predict_proba([[value_to_predict]])[:,1]\n",
"print(value_predicted[0])\n",
"classify(value_predicted, 0.5)[0]"
]
},
{
"cell_type": "markdown",
"id": "b95e30cc-223a-456d-a1d0-82ce7881cfc1",
"metadata": {},
"source": [
"Powinien zdać :)"
]
},
{
"cell_type": "markdown",
"id": "57d01e6b-774d-4ea7-8e3e-3671d6a0e543",
"metadata": {},
"source": [
"A jeśli chcielibyśmy wykorzystać więcej informacji?"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 9,
2023-10-25 13:12:40 +02:00
"id": "a350fa6b-8d99-4fa1-8841-21df8a1cb881",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1.95498874e-08 1.38843757e-06 2.10847410e-03 6.05654169e-02\n",
" 9.89910267e-01 9.99760874e-01 9.47668953e-01 9.99984710e-01\n",
" 9.99999785e-01]\n"
]
},
{
"data": {
"text/plain": [
"array([0, 0, 0, 0, 1, 1, 1, 1, 1])"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 9,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"XX = data[['cwiczenia', 'czas_min']]\n",
" \n",
"model2 = LogisticRegression()\n",
"model2.fit(XX, y)\n",
"y_pred_XX = model2.predict_proba(XX)[:,1]\n",
"print(y_pred_XX)\n",
"\n",
"classify(y_pred_XX, 0.5)"
]
},
{
"cell_type": "markdown",
"id": "5d417fc1-719c-4591-9b25-24fc142021c3",
"metadata": {},
"source": [
"Jak z tego korzystać? Na przykład 15 ćwiczeń i czas w minutach równy 20?"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 10,
2023-10-25 13:12:40 +02:00
"id": "cf332d4b-ff9c-4c59-ad51-7d0ebefd03ef",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.003612177732919793\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 10,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cwiczenia = 15\n",
"czas_min = 20\n",
"\n",
"df = pd.DataFrame(list(zip([cwiczenia], [czas_min])))\n",
"\n",
"value_predicted = model2.predict_proba(df)[:,1]\n",
"print(value_predicted[0])\n",
"classify(value_predicted, 0.5)[0]"
]
},
{
"cell_type": "markdown",
"id": "edd6628a-305d-4acc-802e-5e17485da55c",
"metadata": {},
"source": [
"No tutaj smuteczek :( Ale jakby uczeń spędził więcej czasu?"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 11,
2023-10-25 13:12:40 +02:00
"id": "15506f43-9bb8-4d97-a910-3dae586ac800",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.6246991012300613\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 11,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cwiczenia = 15\n",
"czas_min = 32\n",
"\n",
"df = pd.DataFrame(list(zip([cwiczenia], [czas_min])))\n",
"\n",
"value_predicted = model2.predict_proba(df)[:,1]\n",
"print(value_predicted[0])\n",
"classify(value_predicted, 0.5)[0]"
]
},
{
"cell_type": "markdown",
"id": "0ea82cfe-04d6-499d-a51d-2d47ce070616",
"metadata": {},
"source": [
"To teraz zbudujmy uniwersalny model:"
]
},
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 12,
2023-10-25 13:12:40 +02:00
"id": "54d0a3d7-4194-4657-9553-aa56b199292f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Probability: 0.5132112986552351\n",
"Class: 1\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
2023-10-25 13:20:33 +02:00
"execution_count": 12,
2023-10-25 13:12:40 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def classifyModel(Xargs, y, value_to_predict_column_list, threshold):\n",
" m = LogisticRegression()\n",
" m.fit(Xargs, y)\n",
" d = pd.DataFrame(list(zip(*value_to_predict_column_list)))\n",
" v = m.predict_proba(d)[:,1]\n",
" c = classify(v, 0.5)[0]\n",
" print(\"Probability: \" + str(v[0]))\n",
" print(\"Class: \" + str(c))\n",
" return c\n",
"\n",
"classifyModel(data[['cwiczenia', 'czas_min', 'wejscia']], y, [[15], [32], [3]], 0.5)"
]
},
2023-10-25 13:20:33 +02:00
{
"cell_type": "markdown",
"id": "3f92c938-caae-463d-8058-34f367783308",
"metadata": {},
"source": [
"Zatem mamy teraz funkcję, która dla dowolnego zbioru danych wejściowych i konkretnych wartości, dla których chcemy poznać proponowaną klasę i wybranego przez nas progu prawdopodobieństwa zwróci nam wartość danej klasy jako liczbę.\n",
"\n",
"Na potrzeby tego przedmiotu rozważania na temat problemu klasyfikacji zakończymy na klasyfikacji dwuklasowej (**binarnej**). Nie będziemy się zajmować problemami, gdzie klas jest więcej niż dwie."
]
},
{
"cell_type": "markdown",
"id": "76350b67-55c0-4f6b-8fe0-525e119fb35f",
"metadata": {},
"source": [
"## Zadania\n",
"\n",
"Zadania są dostępne na Moodle'u w sekcji **Zajęcia 04**."
]
},
2023-10-25 13:12:40 +02:00
{
"cell_type": "code",
2023-10-25 13:20:33 +02:00
"execution_count": 13,
"id": "b38b058b-45cb-44e1-98a2-46bc7c2627a9",
2023-10-25 13:12:40 +02:00
"metadata": {},
2023-10-25 12:12:35 +02:00
"outputs": [],
2023-10-25 13:20:33 +02:00
"source": [
"## Jeśli chcesz testuj tutaj swój ewentualny kod i naciśnij Shift + Enter. Możesz też edytować kody wyżej.\n"
]
2023-10-25 12:12:35 +02:00
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.9.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}