[PYTHON] Ich habe versucht, das Bild mithilfe von maschinellem Lernen zu komprimieren

original_compressed.png

Einführung

Komprimieren wir das Bild mit ** K-Means **, einem typischen Clustering-Algorithmus. Zunächst werde ich den K-Means-Algorithmus erläutern. Danach werde ich die Bildkomprimierung mit K-Means erläutern. Informationen zum Inhalt finden Sie unter Coursera Machine Learning.

Was ist Clustering?
Teilen Sie eine Datensammlung entsprechend der Ähnlichkeit zwischen den Daten in mehrere Gruppen (Cluster) ein.

K-Means-Algorithmus

Intuitive Beschreibung des Algorithmus

Der K-Means-Algorithmus kann grob in die folgenden drei Prozesse unterteilt werden:

Lassen Sie uns jeden Schritt mit einem Bild verstehen.

1. Initialisierung des Schwerpunkts

Bestimmen Sie zunächst ** die Position eines Punktes, der als Schwerpunkt bezeichnet wird **. Jeder Schwerpunkt wird als Standardmuster für jeden Cluster betrachtet. Daher benötigen Sie den Schwerpunkt für die Anzahl der Cluster, in die Sie die Daten aufteilen möchten. In der folgenden Abbildung repräsentiert das Sternsymbol den Schwerpunkt und das Kreissymbol die Daten. Da der Schwerpunkt hier drei beträgt, lauten die Daten ** rot ** </ font>, ** blau ** </ font>, ** Green ** </ font> Es wird in 3 Cluster unterteilt.

In dieser Abbildung wird der Schwerpunkt zufällig bestimmt, wir werden jedoch später eine andere Methode einführen. init.png

2. Clusterzuordnung

Nachdem Sie die Position des Schwerpunkts festgelegt haben, besteht der nächste Schritt darin, ** jedem Daten einen Cluster zuzuweisen **. Schauen Sie sich hier die einzelnen Daten und ** red ** </ font>, ** blue ** </ font>, <font color an Weisen Sie einem der Cluster Daten zu, je nachdem, welcher der Cluster-Schwerpunkte von = "grün"> ** grün ** </ font> näher liegt. assignment0.png

3. Neuberechnung des Schwerpunkts

Nachdem Sie den Cluster allen Daten zugewiesen haben, ** berechnen Sie die Position des Schwerpunkts ** neu. Zu diesem Zeitpunkt wird der Durchschnittswert der Daten in jedem Cluster als Position des neuen Schwerpunkts festgelegt. Um beispielsweise den Schwerpunkt von ** green ** </ font> zu berechnen, fügen Sie alle Kreise von ** green ** </ font> hinzu. Zusätzlich wird der Schwerpunkt durch Division durch die Zahl berechnet. Wenn Sie die oberen und unteren Zahlen vergleichen, sehen Sie, dass sich die Position des Schwerpunkts geändert hat. Insbesondere können Sie sehen, dass sich ** blue ** </ font> und ** green ** </ font> bewegen. assignment.png

4. Wiederholen Sie die Schritte 2 und 3

Danach werden die Clusterzuordnung und die Neuberechnung des Schwerpunkts ** wiederholt **. Wie es aussieht, sehen Sie in der Animation unten. Hier ist die Farbe des Schwerpunkts für eine einfache Anzeige schwarz. Am Ende können Sie sehen, dass es konvergiert hat und sich die Position des Schwerpunkts nicht geändert hat. 1449023850iBinuHaXtI5xPbA1449023805.gif

Wenn Sie sich das Standbild ansehen, können Sie auch sehen, dass sich der Schwerpunkt in der folgenden Flugbahn bewegt. result.png

Formalisierung des Algorithmus

Der oben beschriebene K-Means-Algorithmus kann wie folgt im Code zusammengefasst werden.

#Initialisierungsschritt für den Schwerpunkt
centroids = kmeans_init_centroids(X, K)

for iter in range(iterations):

    #Schritte zur Clusterzuweisung:Weisen Sie den Cluster zu, zu dem der Schwerpunkt gehört, der den einzelnen Daten am nächsten liegt.
    #idx ist eine Liste, idx[i]Speichert den Index des Schwerpunkts, der den i-ten Daten zugeordnet ist
    idx = find_closest_centroids(X, centroids)

    #Bewegungsschritt des Schwerpunkts:Berechnen Sie den Durchschnitt im Cluster basierend auf der Schwerpunktzuweisung
    centroids = compute_centroids(X, idx, K)

Der Algorithmus hat zwei Eingaben:

  • Anzahl der Cluster $ K $
  • Trainingssatz {$ x ^ {(1)}, x ^ {(2)}, ..., x ^ {(m)} $}, $ x ^ {(i)} \ in \ mathbb {R} ^ n $

Da K-Means unbeaufsichtigtes Lernen ist, ist der Trainingssatz nicht gekennzeichnet. Jede Trainingsdaten $ x ^ {(i)} $ ist ein $ n $ Dimensionsvektor. Jedes entspricht dem Kreis in der Abbildung. Außerdem muss die Anzahl der Cluster im Voraus festgelegt werden.

Nachdem wir die Eingabedaten kennen, werfen wir einen Blick auf jeden Schritt des Algorithmus.

Initialisierung des Schwerpunkts

Initialisieren Sie zunächst den Schwerpunkt. Der Schwerpunkt ist ein $ n $ -Dimensionsvektor, der durch $ u ^ {(j)} $ dargestellt wird, und es gibt $ K $. In der Abbildung entspricht es dem Punkt des Sterns.

u^{(1)}, u^{(2)},...,u^{(K)} \in \mathbb{R}^n

Als Methode zum Initialisieren des Schwerpunkts aus dem Trainingssatz $ x ^ {(1)}, x ^ {(2)}, ..., x ^ {(m)} \ in \ mathbb {R} ^ n $ Es gibt eine Methode, um $ K $ Stücke zufällig auszuwählen. Wählen Sie nach dem zufälligen Mischen des Trainingssatzes das erste $ K $ als Schwerpunkt aus.

Bei der Implementierung als Code sieht es folgendermaßen aus:

from numpy import *


def kmeans_init_centroids(X, K):
    #Initialisieren Sie so, dass der Schwerpunkt zufällige Daten sind

    #Sortieren Sie die Trainingsdaten nach dem Zufallsprinzip
    rand_X = random.permutation(X)

    #Nehmen Sie das erste K als Schwerpunkt an
    centroids = rand_X[:K]

    return centroids

Clusterzuordnung

Weisen Sie nach der Initialisierung des Schwerpunkts jedem Daten einen Cluster zu. Suchen Sie insbesondere den Schwerpunkt $ u_j $, der den Trainingsdaten $ x ^ {(i)} $ am nächsten liegt, und weisen Sie einen Cluster dieses Schwerpunkts zu.

Um den Schwerpunkt zu finden, der den Daten am nächsten liegt, ermitteln Sie das Quadrat der Entfernung wie folgt. Ordnen Sie eine Gruppe von Schwerpunkten zu, die das Quadrat der Entfernung minimiert.

idx^{(i)} := argmin_j ||x^{(i)} − μ_j||^2

Wobei $ idx ^ {(i)} $ der Index des Schwerpunkts ist, der $ x ^ {(i)} $ am nächsten liegt, und $ u_j $ die Koordinate des $ j $ -ten Schwerpunkts ist.

Eine einfache Implementierung mit numpy sieht folgendermaßen aus:

import sys
from numpy import *


def find_closest_centroids(X, centroids):
    K = centroids.shape[0]
    m = X.shape[0]
    idx = zeros((m, 1))
    for i in range(m):
        lowest = sys.maxint
        lowest_index = 0

        for j in range(K):
            cost = X[i] - centroids[j]
            cost = cost.T.dot(cost)
            if cost < lowest:
                lowest_index = j
                lowest = cost

        idx[i] = lowest_index

    return idx

Mit scipys scipy.spatial.distance.cdist können Sie es wie folgt kurz schreiben. In cdist sind verschiedene Methoden zur Entfernungsberechnung definiert. Diesmal habe ich unter anderem die quadratische Distanz verwendet. Die Leistung ist auch hier besser als der obige naive Code.

import scipy.spatial.distance
from numpy import *


def find_closest_centroids(X, centroids):
    sqdists = scipy.spatial.distance.cdist(centroids, X, 'sqeuclidean')  #Finden Sie den quadratischen Abstand
    idx = argmin(sqdists, axis=0)  #Index des nächstgelegenen Schwerpunkts für jede Daten

    return idx

Neuberechnung des Schwerpunkts

Berechnen Sie den Schwerpunkt neu, nachdem Sie die Daten dem Cluster zugewiesen haben.

Nachdem alle Daten dem Cluster zugewiesen wurden, besteht der nächste Schritt darin, den Schwerpunkt neu zu berechnen. Hier berechnen wir den Durchschnitt der dem Cluster zugewiesenen Daten. Führen Sie für alle Schwerpunkte $ k $ die folgende Formel aus.

u_k := \frac{1}{|C_k|} \sum_{i \in C_k} x^{(i)}

Wobei $ C_k $ der Datensatz ist, dem der Schwerpunkt $ k $ zugewiesen ist. Insbesondere wenn die beiden Daten $ x ^ {(3)} $ und $ x ^ {(5)} $ dem Schwerpunkt $ k = 2 $ zugeordnet sind, dann ist $ u_2 = \ frac {1} {2 } (x ^ {(3)} + x ^ {(5)}) $.

Eine einfache Implementierung würde folgendermaßen aussehen:

from numpy import *


def compute_centroids(X, idx, K):
    m, n = X.shape
    centroids = zeros((K, n))

    for k in range(K):
        temp = X[idx == k]     #Rufen Sie die dem Cluster k zugewiesenen Daten ab
        count = temp.shape[0]  #Anzahl der dem Cluster k zugewiesenen Daten

        for j in range(n):
            centroids[k, j] = sum(temp[:, j]) / count

    return centroids

Wenn Sie es mit der mittleren Funktion von numpy kurz schreiben, sieht es so aus.

from numpy import *


def compute_centroids(X, idx, K):
    m, n = X.shape
    centroids = zeros((K, n))

    for k in range(K):
        centroids[k] = mean(X[idx == k], axis=0)

    return centroids

Bildkompression

Überblick

Nachdem die Erklärung des Algorithmus abgeschlossen ist, gehen wir zum Hauptthema der Bildkomprimierung über. Bilder können auch mithilfe des K-Means-Algorithmus komprimiert werden. Verwenden Sie grob gesagt K-Means, um die Farbe $ K $ auszuwählen, die zur Darstellung des komprimierten Bildes verwendet wird. Es wird komprimiert, indem die Pixel des Originalbilds durch diese $ K $ -Farbe ersetzt werden. Im Folgenden wird es als $ K = 16 $ erklärt.

Wie man Farben ausdrückt

Wenn die Pixel des Bildes eine 24-Bit-Farbdarstellung sind, können sie in 3 8-Bit-Darstellungen zerlegt werden. Jedes 8-Bit ist ** rot ** </ font>, ** blau ** </ font>, ** Es entspricht grün ** </ font> und ist sogenanntes RGB. Da es 8 Bits sind, können Werte von 0 bis 255 ($ 2 ^ 0 bis 2 ^ 8 -1 $) angenommen werden. Aus diesem Grund enthält die Kombination dieser Werte eine Vielzahl von Farben im Bild. Hier reduzieren wir diese Zahl jedoch auf $ K = 16 $ Farben. Durch Reduzieren der Anzahl von Farben auf 16 Farben sollte jedes Pixel durch 4 Bits dargestellt werden können. ($ 16 = 2 ^ 4 $)

Die folgende Abbildung zeigt, dass das Bild rechts erhalten wird, wenn der RGB-Wert jedes im linken Bild enthaltenen Pixels in einem dreidimensionalen Raum abgebildet wird. to3d.png

Anwendung von K-Mitteln

Unter Verwendung des K-Means-Algorithmus wählen wir die 16 Farben aus, die zur Darstellung des komprimierten Bildes verwendet werden. Insbesondere wird jedes Pixel des Originalbildes als Daten eingegeben und unter Verwendung des K-Means-Algorithmus in 16-Farben-Gruppen gruppiert. Sobald der Schwerpunkt berechnet ist, kann das Bild in 16 Farben dargestellt werden, indem der RGB-Wert der Pixel des Originalbilds durch den RGB-Wert des Schwerpunkts ersetzt wird.

Das Bild ist in der folgenden Abbildung dargestellt. In der folgenden Abbildung repräsentiert das Sternsymbol den Schwerpunkt, das Kreissymbol repräsentiert jedes Pixel und die Pixel, die zu demselben Cluster gehören, haben dieselbe Farbe. Zu diesem Zeitpunkt wird der Wert des Pixels, das zu demselben Cluster gehört, durch den Wert des Schwerpunkts dieses Clusters ersetzt. Apropos Bild: Es ist ein Bild, bei dem die Werte der Kreise derselben Farbe zu dem Wert des Cluster-Schwerpunkts aggregiert werden. Zur Vereinfachung der Berechnung wurde der RGB-Wert von 0,0 auf 1,0 konvertiert. kmean3d.png

Die folgende Animation zeigt, wie K-Means tatsächlich auf das Bild angewendet wird. Sie sehen, dass die Clusterzuordnung und die Neuberechnung des Schwerpunkts wiederholt werden. optimize.gif

Das Originalbild und das komprimierte Bild, auf das K-Means tatsächlich angewendet wird. Das linke ist das Originalbild und das rechte ist das komprimierte Bild. Sie können sehen, dass das komprimierte Bild eine gröbere Darstellung mit weniger Farben aufweist. original_compressed.png

Komprimierte Größe

Wenn Sie die 16 typischen Farben gefunden haben, die Ihr Bild darstellen, ersetzen Sie den Wert jedes Pixels durch den Wert des nächstgelegenen Schwerpunkts. Dies entspricht der Darstellung des Originalbilds mit dem jedem Pixel zugewiesenen Schwerpunkt. Da es nur 16 Schwerpunkte gibt, wird die Anzahl der zur Darstellung des Bildes erforderlichen Bits erheblich reduziert. Sehen wir uns die Anzahl der Bits an, die für das Originalbild und das komprimierte Bild erforderlich sind.

Das diesmal verwendete Originalbild ist 128 x 128 Pixel groß. Wir brauchten 24 Informationsbits für jedes Pixel. Insgesamt

128 x 128 x 24 = 393216 bit

Wurde benötigt.

Das komprimierte Bild benötigt die Anzahl der Bits, um 16 Farbinformationen zu speichern. Außerdem benötigt jede Farbe 24 Bit, aber das Bild selbst benötigt nur 4 Bit pro Pixel. Dies liegt daran, dass nur in 4 Bit angezeigt werden muss, welche der 16 Farben verwendet wird. Als Ergebnis am Ende

16 x 24 + 128 x 128 x 4 = 65920 bit

Sie werden es schaffen. Dies ist ungefähr ein Sechstel der Größe des Originalbildes.

Beispielcode

abschließend

Dieses Mal habe ich versucht, das Bild mit dem K-Means-Algorithmus zu komprimieren. Das Bild des komprimierten Ergebnisses ist nicht gut, aber ich denke, es ist ein Algorithmus mit einem breiten Anwendungsbereich. Bitte versuchen Sie es zusammen mit dem Beispielcode.

Referenz

Recommended Posts