"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:"
"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",
"execution_count": null,
"id": "8121eab5-bf59-4994-bef8-08777c9a5728",
"metadata": {},
"outputs": [],
"source": []
},
{
"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:"
"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:"
"No dobrze, to teraz podejmijmy decyzję - czy uczeń zda?"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "ad7e06cb-a996-4734-8225-60072210a631",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0, 0, 0, 0, 1, 1, 1, 1, 1])"
]
},
"execution_count": 6,
"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:"