{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Klasyfikacja w Pythonie" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 1** Które z poniższych problemów jest problemem regresji, a które klasyfikacji?\n", " 1. Sprawdzenie, czy wiadomość jest spamem.\n", " 1. Przewidzenie oceny (od 1 do 5 gwiazdek) na podstawie komentarza.\n", " 1. OCR cyfr: rozpoznanie cyfry z obrazka.\n", " \n", " Jeżeli problem jest klasyfikacyjny, to jakie mamy klasy?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Miary dla klasyfikacji" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Istnieje wieje miar (metryk), na podstawie których możemy ocenić jakość modelu. Podobnie jak w przypadku regresji liniowej potrzebne są dwie listy: lista poprawnych klas i lista predykcji z modelu. Najpopularniejszą z metryk jest trafność, którą definiuje się w następujący sposób:\n", " $$ACC = \\frac{k}{N}$$ \n", " \n", " gdzie: \n", " * $k$ to liczba poprawnie zaklasyfikowanych przypadków,\n", " * $N$ liczebność zbioru testującego." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zadanie** Napisz funkcję, która jako parametry przyjmnie dwie listy (lista poprawnych klas i wyjście z klasyfikatora) i zwróci trafność." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def accuracy_measure(true, predicted):\n", " pass\n", "\n", "true_label = [1, 1, 1, 0, 0]\n", "predicted = [0, 1, 0, 1, 0]\n", "print(\"ACC:\", accuracy_measure(true_label, predicted))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Klasyfikator $k$ najbliższych sąsiadów *(ang. k-nearest neighbors, KNN)*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Klasyfikator [KNN](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm), który został wprowadzony na ostatnim wykładzie, jest bardzo intuicyjny. Pomysł, który stoi za tym klasyfikatorem jest bardzo prosty: Jeżeli mamy nowy obiekt do zaklasyfikowania, to szukamy wśród danych trenujących $k$ najbardziej podobnych do niego przykładów i na ich podstawie decydujemy (np. biorąc większość) do jakie klasy powinien należeć dany obiekt." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "** Przykład 1** Mamy za zadanie przydzielenie obiektów do dwóch klas: trójkątów lub kwadratów. Rozpatrywany obiekt jest zaznaczony zielonym kółkiem. Przyjmując $k=3$, mamy wśród sąsiadów 2 trójkąty i 1 kwadrat. Stąd obiekt powinienm zostać zaklasyfikowany jako trójkąt. Jak zmienia się sytuacja, gdy przyjmiemy $k=5$?\n", "\n", "![Przykład 1](./220px-KnnClassification.svg.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Herbal Iris" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Herbal Iris* jest klasycznym zbiorem danych w uczeniu maszynowym, który powstał w 1936 roku. Zawiera on informacje na 150 egzemplarzy roślin, które należą do jednej z 3 odmian." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 2** Wczytaj do zmiennej ``data`` zbiór *Herbal Iris*, który znajduje się w pliku ``iris.data``. Jest to plik csv." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 3** Odpowiedz na poniższe pytania:\n", " 1. Które atrybuty są wejściowe, a w której kolumnie znajduje się klasa wyjściowa?\n", " 1. Ile jest różnych klas? Wypisz je ekran.\n", " 1. Jaka jest średnia wartość w kolumnie ``sepal_length``? Jak zachowuje się średnia, jeżeli policzymy ją dla każdej z klas osobno?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wytrenujmy klasyfikator *KNN*, ale najpierw przygotujmy dane. Fukcja ``train_test_split`` dzieli zadany zbiór danych na dwie części. My wykorzystamy ją do podziału na zbiór treningowy (66%) i testowy (33%), służy do tego parametr ``test_size``." ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "\n", "X = data.loc[:, 'sepal_length':'petal_width']\n", "Y = data['class']\n", "\n", "(train_X, test_X, train_Y, test_Y) = train_test_split(X, Y, test_size=0.33, random_state=42)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Trenowanie klasyfikatora wygląda bardzo podobnie do treningi modelu regresji liniowej:" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',\n", " metric_params=None, n_jobs=1, n_neighbors=3, p=2,\n", " weights='uniform')" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.neighbors import KNeighborsClassifier\n", "\n", "model = KNeighborsClassifier(n_neighbors=3)\n", "model.fit(train_X, train_Y)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mając wytrenowany model możemy wykorzystać go do predykcji na zbiorze testowym." ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n", "Zaklasyfikowane: Iris-setosa, Orginalne: Iris-setosa\n", "Zaklasyfikowane: Iris-virginica, Orginalne: Iris-virginica\n", "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n", "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n", "Zaklasyfikowane: Iris-setosa, Orginalne: Iris-setosa\n", "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n", "Zaklasyfikowane: Iris-virginica, Orginalne: Iris-virginica\n", "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n", "Zaklasyfikowane: Iris-versicolor, Orginalne: Iris-versicolor\n" ] } ], "source": [ "predicted = model.predict(test_X)\n", "\n", "for i in range(10):\n", " print(\"Zaklasyfikowane: {}, Orginalne: {}\".format(predicted[i], test_Y.reset_index()['class'][i]))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Możemy obliczyć *accuracy*:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.98\n" ] } ], "source": [ "from sklearn.metrics import accuracy_score\n", "\n", "print(accuracy_score(test_Y, predicted))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 4** Wytrenuj nowy model ``model_2`` zmieniając liczbę sąsiadów na 20. Czy zmieniły się wyniki?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 5** Wytrenuj model z $k=1$. Przeprowadź walidację na zbiorze trenującym zamiast na zbiorze testowym? Jakie wyniki otrzymałeś? Czy jest to wyjątek? Dlaczego tak się dzieje?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Walidacja krzyżowa" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zbiór *herbal Iris* jest bardzo małym zbiorem. Wydzielenie z niego zbioru testowego jest obciążone dużą wariancją wyników, tj. w zależności od sposoby wyboru zbioru testowego wyniki mogą się bardzo różnic. Żeby temu zaradzić, stosuje się algorytm [walidacji krzyżowej](https://en.wikipedia.org/wiki/Cross-validation_(statistics). Algorytm wygląda następująco:\n", " 1. Podziel zbiór danych na $n$ części (losowo).\n", " 1. Dla każdego i od 1 do $n$ wykonaj:\n", " 1. Weź $i$-tą część jako zbiór testowy, pozostałe dane jako zbiór trenujący.\n", " 1. Wytrenuj model na zbiorze trenującym.\n", " 1. Uruchom model na danych testowych i zapisz wyniki.\n", " 1. Ostateczne wyniki to średnia z $n$ wyników częściowych. \n", " \n", " W Pythonie służy do tego funkcja ``cross_val_score``, która przyjmuje jako parametry (kolejno) model, zbiór X, zbiór Y. Możemy ustawić parametr ``cv``, który określa na ile części mamy podzielić zbiór danych oraz parametr ``scoring`` określający miarę.\n", " \n", " W poniższym przykładzie dzielimy zbiór danych na 10 części (10-krotna walidacja krzyżowa) i jako miarę ustawiany celność (ang. accuracy)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import cross_val_score\n", "\n", "knn = KNeighborsClassifier(n_neighbors=k)\n", "scores = cross_val_score(knn, X, Y, cv=10, scoring='accuracy')\n", "print(\"Wynik walidacji krzyżowej:\", scores.mean())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**zad. 6** Klasyfikator $k$ najbliższych sąsiadów posiada jeden parametr: $k$, który określa liczbę sąsiadów podczas klasyfikacji. Jak widzieliśmy, wybór $k$ może mieć duże znaczenie dla jakości klasyfikatora. Wykonaj:\n", " 1. Stwórz listę ``neighbors`` wszystkich liczb nieparzystych od 1 do 50.\n", " 1. Dla każdego elementu ``i`` z listy ``neighbors`` zbuduj klasyfikator *KNN* o liczbie sąsiadów równej ``i``. Nastepnie przeprowadz walidację krzyżową (parametry takie same jak powyżej) i zapisz wyniki do tablicy ``cv_scores``.\n", " 1. Znajdź ``k``, dla którego klasyfikator osiąga najwyższy wynik." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wykres przedstawiający precent błedów w zależnosci od liczby sąsiadów." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "# changing to misclassification error\n", "MSE = [1 - x for x in cv_scores]\n", "\n", "# plot misclassification error vs k\n", "plt.plot(neighbors, MSE)\n", "plt.xlabel('Liczba sąsiadów')\n", "plt.ylabel('Procent błędów')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Przejdź teraz do arkusza z zadaniem domowym, gdzie zastosujemy klasyfikator *kNN* na zbiorze danych z pierwszych zajęć." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.7.2" } }, "nbformat": 4, "nbformat_minor": 2 }