SOM (self-organising map)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Bild

Wieso werden 3 Argumente gefordert, wenn es nur a und b als Parameter gibt?
Zuletzt geändert von barisoezcan am Freitag 15. März 2013, 20:48, insgesamt 2-mal geändert.
BlackJack

@barisoezcan: Weil es noch ein Argument gibt: `self`. Du erstellst ein Exemplar vom Typ `SOM` und machst damit überhaupt nichts. Du wolltest das vielleicht an einen Namen binden und dann darauf die Methode aufrufen, statt die (ungebundene) Methode auf der *Klasse* aufzurufen.

Allerdings machen weder Klasse noch ”Methode” hier Sinn.  Warum steckst Du eine Funktion ohne dass das Sinn machen würde in eine Klasse?

Edit: Das ist übrigens kein Compiler-Fehler sondern ein Laufzeitfehler. Und das nächste mal könntest Du das alles vielleicht auch mit kopieren und einfügen als Text hier präsentieren, statt als Bild.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Du hast Recht.. Ich bin ein totaler Python-Neuling..
Das ist nur ein Ausschnitt aus einem Quellcode, der mich gestellt wurde..

Mit:
a = SOM()
print(a.calculateDistance(3, 7))

klappt es nun auch... Vielen Dank!
JonasR
User
Beiträge: 251
Registriert: Mittwoch 12. Mai 2010, 13:59

Hilfreiche Namen sind hilfreich ;D
BlackJack

@barisoezcan: Erklärt immer noch nicht warum das eine Klasse ist.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

BlackJack hat geschrieben:@barisoezcan: Erklärt immer noch nicht warum das eine Klasse ist.
Weil in dieser Klasse noch ein paar weitere Methoden sind..
Und die Klasse stellt eine SOM (self-organising map) dar...
Nachdem ich erst einmal verstanden habe wie sie funktionert, will ich sie testen.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Was für ein Datentyp könnte "data" sein?


...
Methode Anfang:

Code: Alles auswählen

    def train(self, data, weights, dimensions, iterations, epsilon):

        self.nodes = numpy.random.rand(self.size[0], self.size[1], len(data[0]))

        self.weights = weights
        self.epsilon = epsilon

        for i in range(iterations):
            for currentData in data: #training
                self.trainNode(currentData)
Methode Ende
...


"len(data[0])" deutet doch darauf hin, dass "data" eine Liste bzw. String ist, oder?..
Aber weiter unten läuft eine for-Schleife bis "data" und das deutet doch wiederum auf int hin..
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@barisoezcan: Deine Frage deutet darauf hin, dass Du dringend ein Anfängertutorium über for-Schleifen in Python lesen solltest.
BlackJack

@barisoezcan: ``len(data[0])`` deutet darauf hin das es eine Sequenz oder eine Abbildung ist die Elemente enthält die eine Länge haben, also wahrscheinlich selbst Container-Typen sind. Das ist alles was man daraus ersehen kann. Die Schleife geht nicht *bis* `data` sondern *über* `data`, über die einzelnen Elemente in `data`.

Vielleicht solltest Du mal ein Grundlagentutorial zu Python selbst und auch zu Numpy durcharbeiten. Das ist jeweils in den Dokumentationen zu finden.

Gibt es denn keine Dokumentation? Oder zumindest Aufrufe wo ersichtlich ist womit man die Methode aufrufen kann?
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Wo kann ich denn hier ein vernünftiges Tutorium finden?
Und ja, das mit den for-Schleifen habe ich jetzt auch bemerkt..

Code: Alles auswählen

for x in range(y)
und

Code: Alles auswählen

for x in y
ist ein Unterschied :D
Da ich bislang nur ein wenig mit Java und C++ zu tun hatte, habe ich das beim überfliegen übersehen.
BlackJack

@barisoezcan: Wenn man schon ein wenig Programmiererfahrung hat, dann sollte man das Tutorial in der Python-Dokumentation mal durchgearbeitet haben. Das liefert einen ganz guten Überblick über die Programmiersprache. Einmal das Inhaltsverzeichnis der Bibliotheksreferenz durchgehen ist vielleicht auch eine gute Idee.

Bei Numpy gibt es auch ein Tutorial auf der Webseite vom Projekt verlinkt. Da ist insbesondere die „Slicing”-Syntax interessant, die sich anders verhält als „Slicing” bei den Grunddatentypen von Python. Und das man Operationen „parallel” auf alle Werte im Container anwenden kann und keine externen Schleifen dafür schreiben muss.

``for item in iterable: …`` in Python ist grundsätzlich wie ``for (Object item : iterable) { … }``. Syntax für eine Zählschleife a la ``for (int i = 0; i < n; i++) { … }`` gibt es in Python nicht.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

BlackJack hat geschrieben:@barisoezcan: Wenn man schon ein wenig Programmiererfahrung hat, dann sollte man das Tutorial in der Python-Dokumentation mal durchgearbeitet haben. Das liefert einen ganz guten Überblick über die Programmiersprache. Einmal das Inhaltsverzeichnis der Bibliotheksreferenz durchgehen ist vielleicht auch eine gute Idee.

Bei Numpy gibt es auch ein Tutorial auf der Webseite vom Projekt verlinkt. Da ist insbesondere die „Slicing”-Syntax interessant, die sich anders verhält als „Slicing” bei den Grunddatentypen von Python. Und das man Operationen „parallel” auf alle Werte im Container anwenden kann und keine externen Schleifen dafür schreiben muss.

``for item in iterable: …`` in Python ist grundsätzlich wie ``for (Object item : iterable) { … }``. Syntax für eine Zählschleife a la ``for (int i = 0; i < n; i++) { … }`` gibt es in Python nicht.

Vielen Dank für die kompetente Hilfe!
So wie ich das verstanden habe, wird die Schleife

Code: Alles auswählen

for x in b:
"Anzahl der Elemente von b"-mal durchlaufen.
Stimmt das so?
BlackJack

@barisoezcan: Stimmt so. Wobei `b` ein beliebiges iterierbares Objekt sein kann. Das muss also kein Container-Objekt sein, sondern kann auch vorher unbekannt, bis unendlich viele, Elemente liefern.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Grundsätzlich würde ich aber von dem Gedanken, dass die Schleife n-mal durchlaufen wird, weggehen und allgemeiner sagen: Die Schleife läuft so lange, wie der Iterator für das Container-Objekt (oder exakter: das Iterable) noch Elemente ausspuckt. Die erweiterte `for`-Schleife in Java arbeitet ja letztlich auch mittels `it.hasNext()`-Abfragen auf einem Iterator und versucht nicht etwa, auf magische Weise vorab irgendeinen Endwert für `i` festzulegen. Man hat also überhaupt keinen Index-Zähler mehr, mit dessen Hilfe man noch den eigentlichen Zugriff tätigen müsste (`myObjects`), sondern bekommt in solchen Fällen direkt das jeweils nächste Objekt vom Iterator zurückgeliefert. Die `for`-Syntax ist so gesehen nur syntaktischer Zucker, um dem Programmierer das Leben etwas einfacher zu machen.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

snafu hat geschrieben:Grundsätzlich würde ich aber von dem Gedanken, dass die Schleife n-mal durchlaufen wird, weggehen und allgemeiner sagen: Die Schleife läuft so lange, wie der Iterator für das Container-Objekt (oder exakter: das Iterable) noch Elemente ausspuckt. Die erweiterte `for`-Schleife in Java arbeitet ja letztlich auch mittels `it.hasNext()`-Abfragen auf einem Iterator und versucht nicht etwa, auf magische Weise vorab irgendeinen Endwert für `i` festzulegen. Man hat also überhaupt keinen Index-Zähler mehr, mit dessen Hilfe man noch den eigentlichen Zugriff tätigen müsste (`myObjects`), sondern bekommt in solchen Fällen direkt das jeweils nächste Objekt vom Iterator zurückgeliefert. Die `for`-Syntax ist so gesehen nur syntaktischer Zucker, um dem Programmierer das Leben etwas einfacher zu machen.



Hmm gute Erklärung ;) Vielen Dank!
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Code: Alles auswählen

import numpy
import math
import matplotlib.pyplot as plt


class SOM:
    def __init__(self, size):
        assert len(size) == 2
        self.size = size

    def train(self, data, weights, dimensions, iterations, epsilon):
        self.nodes = numpy.random.rand(self.size[0], self.size[1], len(data[0]))
        self.weights = weights
        self.epsilon = epsilon
#        plt.subplot(self.size[0], self.size[1], 1)
#        plt.imshow(self.nodes.reshape(self.size[0], self.size[1], dimensions[0], dimensions[1])[3][3])
#        plt.ion()
#        plt.draw()

        for i in range(iterations):
#            if i % 1 == 0: # plot
#                for x in range(self.size[0]):
#                    for y in range(self.size[1]):
#                        plt.title(str(i))
#                        plt.subplot(self.size[0], self.size[1], x + y * self.size[0] + 1)
#                        plot = plt.imshow(self.nodes.reshape(self.size[0], self.size[1], dimensions[0], dimensions[1])[x][y], interpolation="nearest")
#                        plot.axes.get_xaxis().set_visible(False)
#                        plot.axes.get_yaxis().set_visible(False)
                
#                plt.draw()
#                plt.show()
            for currentData in data: #training
                self.trainNode(currentData)
#        plt.show()
        input('Press Enter to exit')
            

    def trainNode(self, data):
        coordinate = self.findBestMatchingNode(data)
        self.doTraining(data, coordinate)

    def findBestMatchingNode(self, data):
        bestDistance = float("nan")
        result = [0, 0]
        for x in range(self.size[0]):
            for y in range(self.size[1]):
                distance = self.calculateDistance(self.nodes[x][y], data)
                if math.isnan(bestDistance) or distance < bestDistance:
                    bestDistance = distance
                    result = [x, y]
        return result

    def calculateDistance(self, a, b):
        return numpy.sum(pow(a - b, 2) / (a + b)) 

    def doTraining(self, data, coordinate):
        for x in range(self.size[0]):
            for y in range(self.size[1]):
                f = 1 / (1 + pow(coordinate[0] - x, 2) + pow(coordinate[1] - y, 2))
                self.nodes[x][y] = self.nodes[x][y] + self.weights[x][y] * self.epsilon * f * (data - self.nodes[x][y])
Diese Klasse wurde mir gestellt. Sie stellt eine SOM (Self Organising Map) dar und ich habe sie für einen speziellen Anwendungsfall zu benutzen.
Ich bin gerade dabei sie zu analysieren und zu verstehen, aber komme leider an "data" nicht vorbei..
Da in Python die Variablen nicht explizit mit Datentypen deklariert werden, weiss ich nicht, was data (bzw. die Unterelemente der Unterelemente von "data", da "data" ein Containertyp zu sein scheint, die wiederum einen Containertyp enthält) für ein Datentyp sein könnte..

Ein paar Eckdaten:
Die SOM wird für Bilderkennung genutzt. Also gehe ich davon aus, dass "data" sowas wie die Bildpunktdaten enthalten könnte..
Das Auskommentierte ist eher unwichtig, da es zum Plotten verwendet wird.
Und es gibt leider keine Dokumentation oder Kommentare zum Quellcode. :(

Was mich noch ein wenig irritiert:
Die Elemente von "data" werden unverändert über verschiedene Methode bis zur Methode

Code: Alles auswählen

    def calculateDistance(self, a, b):
        return numpy.sum(pow(a - b, 2) / (a + b)) 
übergeben, wo arithmetische Operationen mit ihnen durchgeführt werden.
Wie ist das aber möglich, wenn doch die Elemente von "data" Containertypen sind?
BlackJack

@barisoezcan: Welches `data`? Es gibt da ja verschiedene. Da müsstest Du Dich am besten an den Aufrufen entlang bewegen und versuchen Erkenntnisse aus der Verwendung zu gewinnen. Man kann die Möglichkeiten ja einschränken. Wenn drüber iteriert wird, dann muss es ja iterierbar sein. Wenn Rechenoperationen mit bekannten Typen durchgeführt werden, wie zum Beispiel `self.nodes`, dann schränkt das die möglichen Formen ein. An der Stelle gehe ich mal davon aus, dass es sich Grundsätzlich einmal um `numpy`-Arrays handelt. Würde ja auch zu Bilddaten passen.

Andererseits würde ich das demjenigen um die Ohren hauen von dem es kommt. Selbst mit deklarierten Typen wäre das ohne weitere Erklärungen sehr wahrscheinlich nicht nutzbar ohne da so viel Analysearbeit hinein zu stecken mit der man es auch selbst hätte entwickeln können.

`numpy` benutzt es auch nicht wirklich gut. Da liesse sich sicher einiges an Schleifen aus dem Python-Code verbannen.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

BlackJack hat geschrieben:@barisoezcan: Welches `data`? Es gibt da ja verschiedene. Da müsstest Du Dich am besten an den Aufrufen entlang bewegen und versuchen Erkenntnisse aus der Verwendung zu gewinnen. Man kann die Möglichkeiten ja einschränken. Wenn drüber iteriert wird, dann muss es ja iterierbar sein. Wenn Rechenoperationen mit bekannten Typen durchgeführt werden, wie zum Beispiel `self.nodes`, dann schränkt das die möglichen Formen ein. An der Stelle gehe ich mal davon aus, dass es sich Grundsätzlich einmal um `numpy`-Arrays handelt. Würde ja auch zu Bilddaten passen.

Andererseits würde ich das demjenigen um die Ohren hauen von dem es kommt. Selbst mit deklarierten Typen wäre das ohne weitere Erklärungen sehr wahrscheinlich nicht nutzbar ohne da so viel Analysearbeit hinein zu stecken mit der man es auch selbst hätte entwickeln können.

`numpy` benutzt es auch nicht wirklich gut. Da liesse sich sicher einiges an Schleifen aus dem Python-Code verbannen.

Hmm, ja dankeschön =) Bin jetzt ein großes Stück weitergekommen...

Allerdings tritt nun ein anderes Problem auf...

Ich habe die SOM versucht anzuwenden (mit 2 FITS-Bildern):

Code: Alles auswählen

import numpy
import pyfits
from SOM import SOM


#2 Bilder oeffnen

Bild1 = pyfits.open('Testbilder\Galaxie.fits')
pix1 = Bild1[0].data   #Pixelwerte von Bild1

Bild2 = pyfits.open('Testbilder\Einschlaege.fits')
pix2 = Bild2[0].data   #Pixelwerte von Bild2

#Beide Bilder haben die selbe Größe


data1 = []

for i in range(len(pix1)):
    for j in range(len(pix1[0])):
        data1.append(pix1[i][j])   #Die einzelnen Pixelwerte von Bild1 werden in eine Liste gepackt

data2 = []

for i in range(len(pix2)):
    for j in range(len(pix2[0])):
        data2.append(pix2[i][j])    #Die einzelnen Pixelwerte von Bild2 werden in eine Liste gepackt


#Initialisierungen

size = 1,2   #Anzahl der Nodes (2 Stück)
data = [a,b]   #Bilddaten
weights = [[0.1 for i in range(size[1])] for i in range(size[0])]   #Gewichtung
epsilon = 0.005   #Lernrate
iterations = 1000  #Iterationen
dimensions = len(pix1), len(pix1[0])   #Größe der Bilder


#Instanz der Klasse SOM

koh = SOM(size)
koh.train(data,weights,dimensions,iterations,epsilon)[0][0]
Wenn ich das aber so ausführe, dann wird immer nur mit dem einen Bild, welches den geringsten "Abstand" zu den beiden "Nodes" hat, "trainiert".
Das andere Bild wird sozusagen gar nicht berücksichtigt..
Aber es sollte ja eigentlich beim einen "Node" das eine Bild erscheinen, beim anderen "Node" das andere Bild (nach einer gewissen Anzahl von Iterationen) ...

Die beiden Bilder waren:
Bild
Bild

So sieht es dann aus:
Bild

Wie man erkennt, stellen die beiden Nodes nur das erste Bild dar..
Zuletzt geändert von barisoezcan am Sonntag 24. März 2013, 23:09, insgesamt 1-mal geändert.
barisoezcan
User
Beiträge: 73
Registriert: Freitag 15. März 2013, 19:38

Das wäre dann nochmal die komplette Klasse SOM, von der eine Instanz erzeugt wird...

Code: Alles auswählen

import numpy
import math
import matplotlib.pyplot as plt


class SOM:
    def __init__(self, size):
        assert len(size) == 2
        self.size = size

    def train(self, data, weights, dimensions, iterations, epsilon):
        self.nodes = numpy.random.rand(self.size[0], self.size[1], len(data[0]))
        self.weights = weights
        self.epsilon = epsilon
        plt.subplot(self.size[0], self.size[1], 1)
        plt.imshow(self.nodes.reshape(self.size[0], self.size[1], dimensions[0], dimensions[1])[0][0])
        plt.ion()
        plt.draw()

        for i in range(iterations):
            if i % 1 == 0: # plot
                for x in range(self.size[0]):
                    for y in range(self.size[1]):
                        plt.title(str(i))
                        plt.subplot(self.size[0], self.size[1], x + y * self.size[0] + 1)
                        plot = plt.imshow(self.nodes.reshape(self.size[0], self.size[1], dimensions[0], dimensions[1])[x][y], interpolation="nearest")
                        plot.axes.get_xaxis().set_visible(False)
                        plot.axes.get_yaxis().set_visible(False) 
                plt.draw()
                plt.show()
            for currentData in data: #training
                self.trainNode(currentData)
        plt.show()
        input('Press Enter to exit')
            

    def trainNode(self, data):
        coordinate = self.findBestMatchingNode(data)
        self.doTraining(data, coordinate)

    def findBestMatchingNode(self, data):
        bestDistance = float("nan")
        result = [0, 0]
        for x in range(self.size[0]):
            for y in range(self.size[1]):
                distance = self.calculateDistance(self.nodes[x][y], data)
                if math.isnan(bestDistance) or distance < bestDistance:
                    bestDistance = distance
                    result = [x, y]
        return result

    def calculateDistance(self, a, b):
        return numpy.sum(pow(a - b, 2) / (a + b)) 

    def doTraining(self, data, coordinate):
        for x in range(self.size[0]):
            for y in range(self.size[1]):
                f = 1 / (1 + pow(coordinate[0] - x, 2) + pow(coordinate[1] - y, 2))
                self.nodes[x][y] = self.nodes[x][y] + self.weights[x][y] * self.epsilon * f * (data - self.nodes[x][y])
                
                
Antworten