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

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