Repräsentierung und Visualisierung von Daten

Einleitung

Visualisierung und Reräsentierung von Daten

Beim maschinellen Lernen geht es darum, Modelle an Daten anzupassen. Aus diesem Grund beginnen wir damit zu zeigen wie Daten dargestellt werden können, um vom Computer verstanden zu werden.

Zu Beginn dieses Kapitels haben wir Tom Mitchells Definition des maschinellen Lernens zitiert: Ein wohlgestelltes Lernproblem: Ein Computerprogramm soll aus der Erfahrung E in Bezug auf eine Aufgabe T und ein Leistungsmaß P lernen, wenn sich seine Leistung auf T, gemessen durch P, mit Erfahrung E verbessert. Daten sind der "Rohstoff" für maschinelles Lernen. ML lernt aus Daten. In Mitchells Definition sind "Daten" hinter den Begriffen "Erfahrung E" und "Leistungsmaß P" verborgen. Wie bereits erwähnt, benötigen wir gelabelte Daten, um unseren Algorithmus zu trainieren und zu testen.

Es empfiehlt sich jedoch, dass man sich mit seinen Daten vertraut macht, bevor man mit dem Training des Klassifikators beginnt.

Numpy bietet ideale Datenstrukturen zur Darstellung der Daten und Matplotlib bietet hervorragende Möglichkeiten zur Visualisierung der Daten. Wie man hierbei vorgeht, wollen wir im Folgenden anhand der Daten zeigen, die sich im Modul sklearn befinden.

Ein einfaches Beispiel: das Iris-Datenset

Was war das erste Programm, das Sie gesehen haben? Ich wette, es könnte ein Programm gewesen sein, das "Hello World" in einer Programmiersprache ausgegeben hat. Höchstwahrscheinlich habe ich recht. Fast jedes Einführungsbuch oder Tutorial zur Programmierung beginnt mit einem solchen Programm. Es ist eine Tradition, die auf das Buch "The C Programming Language" von 1968 von Brian Kernighan und Dennis Ritchie zurückgeht!

Die Wahrscheinlichkeit, dass der erste Datensatz, den Sie in einem Einführungstutorial zum maschinellen Lernen sehen, der "Iris-Datensatz" ist, ist ähnlich hoch. Der Iris-Datensatz enthält die Messungen von 150 Irisblüten aus 3 verschiedenen Arten: Als Beispiel für einen einfachen Datensatz sehen wir uns die von scikit-learn gespeicherten Irisdaten an. Die Daten bestehen aus Messungen von drei verschiedenen Irisblütenarten. Es gibt drei verschiedene Arten von Schwertlilien (Iris) in diesem speziellen Datensatz wie unten dargestellt:

Iris Setosa

Iris Versicolor

Iris Virginica

Der Iris-Datensatz wird häufig wegen seiner Einfachheit verwendet. Dieser Datensatz ist in scikit-learn enthalten.

Die Daten bestehen aus den ermittelten Abmessungen der Kronblätter (Fachbegriff: Petalum oder engl. petal) und der Kelchblätter (Fachbegriff: Sepalum) von drei verschiedenen Lilienarten:

  • Merkmale im Iris-Datensatz:

    1. Kelchlänge (sepal length) in cm
    2. Kelchbreite (sepal width) in cm
    3. Kronblattlänge (petal length) in cm
    4. Kronblattbreite (petal width) in cm
  • Zu prognostizierende Zielklassen:

    1. Iris Setosa
    2. Iris Versicolour
    3. Iris Virginica

Sepals und Petals, Abmessungen

scikit-learn beinhaltet eine Kopie der Iris-CSV-Datei zusammen mit einer Hilfsfunktion, um diese Daten in numpy-Arrays zu laden:

from sklearn.datasets import load_iris
iris = load_iris()

Der resultierende Datensatz ist ein "Bunch"-Objekt:

type(iris)
Ausgabe: :

sklearn.utils.Bunch

The resulting dataset is a Bunch object: you can see what's available using the method keys():

iris.keys()
Ausgabe: :

dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

Ein Bunch-Objekt ähnelt einem Dictionary, ermöglicht jedoch zusätzlich den Zugriff auf die Schlüssel im Attributstil:

In [ ]:
print(iris["target_names"])
print(iris.target_names)

iris.data.shape enthält die Anzahl der Merkmale und Samples (Stichproben):

n_samples, n_features = iris.data.shape
print('Anzahl der Sampels:', n_samples)
print('Anzahl der Merkmale:', n_features)
# the sepal length, sepal width, petal length and petal width of the first sample (first flower)
print(iris.data[0])
Anzahl der Sampels: 150
Anzahl der Merkmale: 4
[5.1 3.5 1.4 0.2]

Die Merkmale jeder Blume werden im Attribut data des Datensatzes gespeichert. Werfen wir einen Blick auf einige der Sampels:

# Blumen mit den Indizes 12, 26, 89 und 114
iris.data[[12, 26, 89, 114]]
Ausgabe: :

array([[4.8, 3. , 1.4, 0.1],
       [5. , 3.4, 1.6, 0.4],
       [5.5, 2.5, 4. , 1.3],
       [5.8, 2.8, 5.1, 2.4]])

Die Informationen über die Klasse jeder Probe, d.h. die Labels, werden im Attribut "target" des Datensatzes gespeichert:

print(iris.data.shape)
print(iris.target.shape)
(150, 4)
(150,)
print(iris.target)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]
import numpy as np
np.bincount(iris.target)
Ausgabe: :

array([50, 50, 50])

Mit der Bincount-Funktion von NumPy (oben) können wir sehen, dass die Klassen in diesem Datensatz gleichmäßig verteilt sind - es gibt 50 Blumen von jeder Art, wobei

  • class 0: Iris-Setosa
  • class 1: Iris-Versicolor
  • class 2: Iris-Virginica

Diese Klassennamen werden im letzten Attribut gespeichert, nämlich target_names:

print(iris.target_names)
['setosa' 'versicolor' 'virginica']

Daten in sklearn (scikit-learn)

Bei den Daten in scikit-learn kann man mit wenigen Ausnahmen davon ausgehen, dass sie als zwei-dimensionale Arrays abgespeichert sind. Ihre Gestalt (shape) ist von der Form "(n_samples, n_features)".

  • n_samples: Die Anzahl der Samples: Jedes Sample ist ein zu verarbeitendes Objekt. Eine Stichprobe kann ein Dokument, ein Bild, ein Ton, ein Video oder ein astronomisches Objekt sein, Eine Stichprobe entspricht einer Zeile in einer Datenbank oder CSV-Datei, oder was auch immer man mit einem festen Satz von quantitativen Merkmalen beschreiben können.
  • n_features: Die Anzahl der Merkmale oder besonderen Merkmale, die zur Beschreibung der einzelnen Merkmale verwendet werden können Artikel in quantitativer Weise. Features sind im Allgemeinen reelle Werte, können aber auch boolesche Werte oder Werte sein in einigen Fällen diskret bewertet.

Die Anzahl der Features muss im Voraus festgelegt werden. Es kann jedoch sehr hoch dimensioniert sein (z.B. Millionen von Merkmalen), wobei die meisten von ihnen "Null" für eine gegebene Stichprobe sein können. Das ist ein Fall wo scipy.sparse Matrizen nützlich sein können, sind sie viel speichereffizienter als NumPy-Arrays.

Wie wir uns aus dem vorherigen Abschnitt (oder dem Jupyter-Notizbuch) erinnern, stellen wir Beispiele (Datenpunkte oder Instanzen) als Zeilen im Datenarray dar und speichern die entsprechenden Features, die "Dimensionen", als Spalten.

Die Daten für eine Blumen bestehen aus einer Zeile im Datenarray, und die Spalten (Merkmale) geben die Blumenmaße in Zentimetern an. Zum Beispiel können wir diesen Iris-Datensatz, bestehend aus 150 Beispielen und 4 Merkmalen, einem zweidimensionalen Array oder einer Matrix $\mathbb{R}^{150 \times 4}$ , im folgenden Format darstellen:

$$\mathbf{X} = \begin{bmatrix} x_{1}^{(1)} & x_{2}^{(1)} & x_{3}^{(1)} & x_{4}^{(1)} \\ x_{1}^{(2)} & x_{2}^{(2)} & x_{3}^{(2)} & x_{4}^{(2)} \\ \vdots & \vdots & \vdots & \vdots \\ x_{1}^{(150)} & x_{2}^{(150)} & x_{3}^{(150)} & x_{4}^{(150)} \end{bmatrix}. $$

Der hochgestellte Index bezeichnet die i-te Zeile, und der tiefgestellte Index bezeichnet das j-te Merkmal.

Im Allgemeinen haben wir $n$ Zeilen und $k$ Spalten:

$$\mathbf{X} = \begin{bmatrix} x_{1}^{(1)} & x_{2}^{(1)} & x_{3}^{(1)} & \dots & x_{k}^{(1)} \\ x_{1}^{(2)} & x_{2}^{(2)} & x_{3}^{(2)} & \dots & x_{k}^{(2)} \\ \vdots & \vdots & \vdots & \vdots & \vdots \\ x_{1}^{(n)} & x_{2}^{(n)} & x_{3}^{(n)} & \dots & x_{k}^{(n)} \end{bmatrix}. $$
print(iris.data.shape)
print(iris.target.shape)
(150, 4)
(150,)

Visualisierung der Merkmale des Iris-Datensatzes

Diese Daten sind vierdimensional, aber wir können eine oder zwei der Dimensionen visualisieren zu einem Zeitpunkt mit einem einfachen Histogramm oder Streudiagramm.

Histograms of the features

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x_index = 3
colors = ['blue', 'red', 'green']
for label, color in zip(range(len(iris.target_names)), colors):
    ax.hist(iris.data[iris.target==label, x_index], 
            label=iris.target_names[label],
            color=color)
ax.set_xlabel(iris.feature_names[x_index])
ax.legend(loc='upper right')
fig.show()

Übung

Im obigen Code haben wir x_index auf 3 gesetzt, das bedeutet, dass wir uns das Histogramm der Breite der Kronblätter (petals) erzeugt haben. Schauen Sie sich entsprechend die Histogramme der anderen Größen von den Kelch- (sepals) und Kronblättern an.

Streudiagramm mit zwei Merkmalen

Das nächste Diagramm zeigt zwei Merkmale in einem Diagramm:

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x_index = 3
y_index = 0
colors = ['blue', 'red', 'green']
for label, color in zip(range(len(iris.target_names)), colors):
    ax.scatter(iris.data[iris.target==label, x_index], 
                iris.data[iris.target==label, y_index],
                label=iris.target_names[label],
                c=color)
ax.set_xlabel(iris.feature_names[x_index])
ax.set_ylabel(iris.feature_names[y_index])
ax.legend(loc='upper left')
plt.show()

Streudiagramm Matrix

Anstatt die Daten jeweils als einzelne Plots zu betrachten, können wir ein gängiges von Analysten verwendetes Tool vewenden. Es erzeugt eine Streudiagramm-Matrix (Scatterplot-Matrix).

Streudiagramm-Matrizen zeigen Streudiagramme zwischen allen Features im Datensatz sowie Histogramme, um die Verteilung der einzelnen Features anzuzeigen.

import pandas as pd
    
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
pd.plotting.scatter_matrix(iris_df, 
                           c=iris.target, 
                           figsize=(8, 8)
                          );

3-Dimensionale Visualisierung

import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from mpl_toolkits.mplot3d import Axes3D
iris = load_iris()
X = []
for iclass in range(3):
    X.append([[], [], []])
    for i in range(len(iris.data)):
        if iris.target[i] == iclass:
            X[iclass][0].append(iris.data[i][0])
            X[iclass][1].append(iris.data[i][1])
            X[iclass][2].append(sum(iris.data[i][2:]))
colours = ("r", "g", "y")
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for iclass in range(3):
    ax.scatter(X[iclass][0], X[iclass][1], X[iclass][2], c=colours[iclass])
plt.show()

Andere verfügbaren Datensätze

Scikit-learn stellt eine Vielzahl von Datensätzen zum Testen von Lernalgorithmen zur Verfügung.

Sie kommen in drei Geschmacksrichtungen:

  • Gepackte Daten: Diese kleinen Datensätze sind Teil der Installation von scikit-learn
  • Herunterladbare Daten: Diese größeren Datensätze stehen zum Download zur Verfügung und können per Scikit gelernt werden Enthält Tools, die diesen Prozess optimieren. Diese Tools finden Sie in sklearn.datasets.fetch_ *
  • Generierbare Daten: Es gibt mehrere Datensätze, die auf Modellen basieren und mittels seed-Werten generiert werden können auf a generiert werden können. Diese sind als sklearn.datasets.make_ * verfügbar

Mittels der Tab-Vervollständigungsfunktion von IPython können Sie die verfügbaren Dataset Loader, Fetcher und Generatoren nach dem Import des datasets Submoduls aus sklearn untersuchen,

datasets.load_<TAB>

or

datasets.fetch_<TAB>

or

datasets.make_<TAB>
from sklearn import datasets

Warnung: Viele dieser Datasets sind sehr groß und das Herunterladen kann sehr lange dauern!

Digits-Datenset laden

Wir werden uns nun einen dieser Datensätze genauer ansehen. Wir schauen uns den Ziffern-Datensatz an. Wir laden ihn zuerst:

from sklearn.datasets import load_digits
digits = load_digits()

Wir können uns wieder einen Überblick über die verfügbaren Attribute verschaffen, indem wir uns die "keys" anschauen:

digits.keys()
Ausgabe: :

dict_keys(['data', 'target', 'target_names', 'images', 'DESCR'])

Werfen wir einen Blick auf die Anzahl der Elemente und Funktionen:

n_samples, n_features = digits.data.shape
print((n_samples, n_features))
(1797, 64)
print(digits.data[0])
print(digits.target)
[ 0.  0.  5. 13.  9.  1.  0.  0.  0.  0. 13. 15. 10. 15.  5.  0.  0.  3.
 15.  2.  0. 11.  8.  0.  0.  4. 12.  0.  0.  8.  8.  0.  0.  5.  8.  0.
  0.  9.  8.  0.  0.  4. 11.  0.  1. 12.  7.  0.  0.  2. 14.  5. 10. 12.
  0.  0.  0.  0.  6. 13. 10.  0.  0.  0.]
[0 1 2 ... 8 9 8]

Die Daten liegen auch unter digits.images vor. Dabei handelt es sich um die Rohdaten der Bilder in der Form 8 Zeilen und 8 Spalten.

Bei "data" entspricht ein Bild einem eindimensionen Numpy-Array mit der Länge 64, und images enthält 2-dimensionale numpy-Arrays mit der Shape (8, 8)

print("Shape eines Items: ", digits.data[0].shape)
print("Datentype eines Items: ", type(digits.data[0]))
print("Shape eines Items: ", digits.images[0].shape)
print("Datentype eines Items: ", type(digits.images[0]))
Shape eines Items:  (64,)
Datentype eines Items:  <class 'numpy.ndarray'>
Shape eines Items:  (8, 8)
Datentype eines Items:  <class 'numpy.ndarray'>

Nun wollen wir die Daten visualisieren:

import matplotlib.pyplot as plt
# set up the figure
fig = plt.figure(figsize=(6, 6))  # figure size in inches
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot der Ziffern: 
# jedes Bild besteht aus 8x8 Pixel.
for i in range(64):
    ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
    ax.imshow(digits.images[i], cmap=plt.cm.binary, interpolation='nearest')
    
    # label the image with the target value
    ax.text(0, 7, str(digits.target[i]))

Übungen

Aufgabe 1

sklearn enthält einen „Wein-Datensatz“.

  • Suchen und laden Sie diesen Datensatz
  • Können Sie eine Beschreibung finden?
  • Wie heißen die Klassen und die Features?
  • Wo sind die Daten und die Daten und die Labels?

Aufgabe 2

Erzeugen Sie einen scatter-Plot mit den Features ash und color_intensity des Wine-Datensatzes.

Aufgabe 3

Erzeugen Sie eine Scatter-Matrix für das Wine-Datenset

Aufgabe 4

Finden und laden Sie den Olivetti-Datensatz der Photos mit Gesichtern enthält und stellen Sie die Gesichter dar.

Lösungen

Lösung zu Aufgabe 1

Laden des "Wine"-Datensets:

from sklearn import datasets
wine = datasets.load_wine()

Die BEschreibung erhält man mittels:

print(wine.DESCR)
.. _wine_dataset:
Wine recognition dataset
------------------------
**Data Set Characteristics:**
    :Number of Instances: 178 (50 in each of three classes)
    :Number of Attributes: 13 numeric, predictive attributes and the class
    :Attribute Information:
 		- Alcohol
 		- Malic acid
 		- Ash
		- Alcalinity of ash  
 		- Magnesium
		- Total phenols
 		- Flavanoids
 		- Nonflavanoid phenols
 		- Proanthocyanins
		- Color intensity
 		- Hue
 		- OD280/OD315 of diluted wines
 		- Proline
    - class:
            - class_0
            - class_1
            - class_2
		
    :Summary Statistics:
    
    ============================= ==== ===== ======= =====
                                   Min   Max   Mean     SD
    ============================= ==== ===== ======= =====
    Alcohol:                      11.0  14.8    13.0   0.8
    Malic Acid:                   0.74  5.80    2.34  1.12
    Ash:                          1.36  3.23    2.36  0.27
    Alcalinity of Ash:            10.6  30.0    19.5   3.3
    Magnesium:                    70.0 162.0    99.7  14.3
    Total Phenols:                0.98  3.88    2.29  0.63
    Flavanoids:                   0.34  5.08    2.03  1.00
    Nonflavanoid Phenols:         0.13  0.66    0.36  0.12
    Proanthocyanins:              0.41  3.58    1.59  0.57
    Colour Intensity:              1.3  13.0     5.1   2.3
    Hue:                          0.48  1.71    0.96  0.23
    OD280/OD315 of diluted wines: 1.27  4.00    2.61  0.71
    Proline:                       278  1680     746   315
    ============================= ==== ===== ======= =====
    :Missing Attribute Values: None
    :Class Distribution: class_0 (59), class_1 (71), class_2 (48)
    :Creator: R.A. Fisher
    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
    :Date: July, 1988
This is a copy of UCI ML Wine recognition datasets.
https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data
The data is the results of a chemical analysis of wines grown in the same
region in Italy by three different cultivators. There are thirteen different
measurements taken for different constituents found in the three types of
wine.
Original Owners: 
Forina, M. et al, PARVUS - 
An Extendible Package for Data Exploration, Classification and Correlation. 
Institute of Pharmaceutical and Food Analysis and Technologies,
Via Brigata Salerno, 16147 Genoa, Italy.
Citation:
Lichman, M. (2013). UCI Machine Learning Repository
[http://archive.ics.uci.edu/ml]. Irvine, CA: University of California,
School of Information and Computer Science. 
.. topic:: References
  (1) S. Aeberhard, D. Coomans and O. de Vel, 
  Comparison of Classifiers in High Dimensional Settings, 
  Tech. Rep. no. 92-02, (1992), Dept. of Computer Science and Dept. of  
  Mathematics and Statistics, James Cook University of North Queensland. 
  (Also submitted to Technometrics). 
  The data was used with many others for comparing various 
  classifiers. The classes are separable, though only RDA 
  has achieved 100% correct classification. 
  (RDA : 100%, QDA 99.4%, LDA 98.9%, 1NN 96.1% (z-transformed data)) 
  (All results using the leave-one-out technique) 
  (2) S. Aeberhard, D. Coomans and O. de Vel, 
  "THE CLASSIFICATION PERFORMANCE OF RDA" 
  Tech. Rep. no. 92-01, (1992), Dept. of Computer Science and Dept. of 
  Mathematics and Statistics, James Cook University of North Queensland. 
  (Also submitted to Journal of Chemometrics).

Die Namen der Klassen und der Features erhalten wir mit den Attributen "target_names" und "feature_names":

print(wine.target_names)
print(wine.feature_names)
['class_0' 'class_1' 'class_2']
['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']
daten = wine.data
gelabelte_daten = wine.target

Lösung zu Aufgabe 2

from sklearn import datasets
import matplotlib.pyplot as plt
wine = datasets.load_wine()
features = 'ash', 'color_intensity'
features_index = [wine.feature_names.index(features[0]),
                  wine.feature_names.index(features[1])]
colors = ['blue', 'red', 'green']
for label, color in zip(range(len(wine.target_names)), colors):
    plt.scatter(wine.data[wine.target==label, features_index[0]], 
                wine.data[wine.target==label, features_index[1]],
                label=wine.target_names[label],
                c=color)
plt.xlabel(features[0])
plt.ylabel(features[1])
plt.legend(loc='upper left')
plt.show()

Lösung zu Aufgabe 3

import pandas as pd
from sklearn import datasets
wine = datasets.load_wine()
def rotate_labels(df, axes):
    """ changing the rotation of the label output, 
    y labels horizontal and x labels vertical """
    n = len(df.columns)
    for x in range(n):
        for y in range(n):
            # to get the axis of subplots
            ax = axs[x, y]
            # to make x axis name vertical  
            ax.xaxis.label.set_rotation(90)
            # to make y axis name horizontal 
            ax.yaxis.label.set_rotation(0)
            # to make sure y axis names are outside the plot area
            ax.yaxis.labelpad = 50
wine_df = pd.DataFrame(wine.data, columns=wine.feature_names)
axs = pd.plotting.scatter_matrix(wine_df, 
                                 c=wine.target, 
                                 figsize=(8, 8),
                                );
rotate_labels(wine_df, axs)

Lösung zur Aufgabe 4

from sklearn.datasets import fetch_olivetti_faces
# Laden des Datensatzes
faces = fetch_olivetti_faces()
downloading Olivetti faces from https://ndownloader.figshare.com/files/5976027 to /home/bernd/scikit_learn_data
faces.keys()
Ausgabe: :

dict_keys(['data', 'images', 'target', 'DESCR'])
n_samples, n_features = faces.data.shape
print((n_samples, n_features))
(400, 4096)
np.sqrt(4096)
Ausgabe: :

64.0
faces.images.shape
Ausgabe: :

(400, 64, 64)
faces.data.shape
Ausgabe: :

(400, 4096)
print(np.all(faces.images.reshape((400, 4096)) == faces.data))
True
# set up the figure
fig = plt.figure(figsize=(6, 6))  # figure size in inches
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot the digits: each image is 8x8 pixels
for i in range(64):
    ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
    ax.imshow(faces.images[i], cmap=plt.cm.bone, interpolation='nearest')
    
    # label the image with the target value
    ax.text(0, 7, str(faces.target[i]))