Python2019/labs05/sklearn cz. 2.ipynb

395 lines
12 KiB
Plaintext
Raw Normal View History

2019-02-10 08:39:58 +01:00
{
"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
}