Problem bei Paralellisierung



  • Ich habe einen Bereich im Code der parallelisiert werden soll.
    Allerdings ist eine Stelle nicht gescheit parallelisierbar, da der errechnete Wert vom aktuellen Durchgang abhängig ist.

    m_fSigmaT = m_DistFunction->decay(m_fSigma0, m_iCycle, m_fLambda);
    

    Nun frage ich mich, ob es möglich ist, dieses problem irgendwie mit hilfe von openMP zu umgehen.
    Oder ist dieses Beispiel eines der Probleme, die man nicht parallelisieren kann, ohne Genauigkeit einzubüßen?

    #pragma omp for
    	for(int i = 0; i < static_cast<int>(m_iCycles); i++) {
    		m_iCycle = i;
    		std::cout<<"Cycle: "<<m_iCycle<<std::endl;
    
    	    // The input vectors are presented to the network at random
    		iChoice = RandInt(iMin, iMax);
    	    SetInput( GetTrainingSet()->GetInput(iChoice) );
    
    		// Present the input vector to each node and determine the BMU
    		FindBMNeuron();
    
    		// Calculate the width of the neighborhood for this time step
    		m_fSigmaT = m_DistFunction->decay(m_fSigma0, m_iCycle, m_fLambda);
    
    		// Adjust the weight vector of the BMU and its neighbors
    		PropagateBW();
    	}
    

  • Mod

    Wenn ich das recht sehen, hängt doch nichts von den vorherigen Durchgängen ab. Wo ist das Problem?



  • Wenn ich den kram mit openmp ausführe, dann laufen alle threads mit unterschiedlicher indexnummer ab.
    das führt dann dazu, dass werte abwechselnd hintereinander mit total unterschiedlicher stärke trainiert werden.
    das sollte vermieden werden.


  • Mod

    Dann ist mir deine Problemstellung unklar. Versuch mal ein kurzes, vollständiges(!) Programm zu basteln, an dem dein Fehler zu sehen ist. Sag dazu auch noch, was genau du erwartest (am besten eine konkrete Ausgabe, die kommen soll) und was stattdessen passiert.



  • Hmm, irgendwie scheint das Problem doch eher in einer Unterfunktion zu liegen.
    Aber ich versuche das ganze mal so gut wie möglich darzulegen.
    Ich habe eine selbstorganisierende Karte die Trainiert wird, durch die Funktion:

    void SOMNet::Training(const unsigned int &iCycles) {
    	m_iCycles = iCycles;
    	int iChoice = 0;
    	int iMin 	= 0;
    	int iMax 	= GetTrainingSet()->GetNrElements()-1;
    
    	// find sigma0
    	assert(iCycles > 0);
    	assert(m_fSigma0 > 0.f);
    	m_fLambda = m_iCycles / log(m_fSigma0);
    
    	for(int i = 0; i < static_cast<int>(m_iCycles); i++) {
    		m_iCycle = i;
    		std::cout<<"Cycle: "<<m_iCycle<<std::endl;
    
    	    // The input vectors are presented to the network at random
    		iChoice = RandInt(iMin, iMax);
    	    SetInput( GetTrainingSet()->GetInput(iChoice) );
    
    		// Present the input vector to each node and determine the BMU
    		FindBMNeuron();
    
    		// Calculate the width of the neighborhood for this time step
    		m_fSigmaT = m_DistFunction->decay(m_fSigma0, m_iCycle, m_fLambda);
    
    		// Adjust the weight vector of the BMU and its neighbors
    		PropagateBW();
    	}
    }
    

    Dann habe ich eine Unterfunktion bei der der Hauptrechenaufwand liegt:

    //#pragma omp parallel for
    	for(int i = 0; i < static_cast<int>(m_pOPLayer->GetNeurons().size() ); i++) {
    		pNeuron = (SOMNeuron*)m_pOPLayer->GetNeuron(i);
    		fDist = pNeuron->GetDistance2Neur(*m_pBMNeuron);
    
    		if(fDist < m_fSigmaT) {
    			//calculate by how much weights get adjusted ..
    			fInfluence = m_DistFunction->distance(fDist, m_fSigmaT);
    			pNeuron->SetInfluence(fInfluence);
    
    			// .. and adjust them
    			pNeuron->AdaptEdges();
    		}
    
    	    //reduce the learning rate
    		fLearningRate = m_DistFunction->decay(m_fLearningRate, m_iCycle, m_iCycles);
    		pNeuron->SetLearningRate(fLearningRate);
    	}
    
    void SOMNeuron::AdaptEdges() {
    	Edge 	*pEdge 	= NULL;
    	float 	fInput 	= 0.f;
    	float 	fWeight = 0.f;
    
        for(unsigned int i = 0; i < GetConsI().size(); i++) {
        	pEdge 	= GetConI(i);
        	fWeight = *pEdge;
        	fInput 	= *pEdge->GetDestination(this);
    
        	pEdge->SetValue(fWeight + (m_fInfluence*m_fLearningRate*(fInput-fWeight) ) );
        }
    }
    

    Wenn ich das Netz regulär auf ein paar Farben trainiere, dann sieht das Ergebnis richtig aus, da alle Farben bestimmten und abgegrenzten Bereichen zugeordnet werden:
    http://s14.directupload.net/images/120308/8r92wjkw.png
    Wenn ich jetzt OpenMP benutze, dann sieht das ganze fast immer so aus:
    http://s1.directupload.net/images/120308/gtuomo4j.png
    Ehrlich gesagt bin ich mir jetzt auch nicht mehr sicher woran es liegt.
    Wenn ich die Lernrate konstant setze, bekomme ich die gleichen Resultate in beiden Fällen,
    sodass es an etwas anderen liegen muss. Ich weis jetzt auch nicht wo das Problem liegt, aber die Ausgabebilder demonstrieren, dass es eins gibt, wenn ich OpenMP benutze.


  • Mod

    Nur um mal ganz dumme Fehler auszuschließen: Du arbeitest da ja immer auf den gleichen Objekten (m_pOPLayer, m_DistFunction). Sind die auch richtig an die Threads verteilt (private, shared)? Falls sie shared sind, sind die benutzten Methoden auch threadsicher? Falls die Methoden Änderungen vornehmen, ist deren Reihenfolge auch egal?

    P.S.: Und tut die ungarische Notation Not? Das ist in C++ ziemlich unsinnig, da man es sowieso nicht konsequent durchhalten kann ohne verrückt zu werden und macht alles bloß total unleserlich.



  • Die Notation habe ich in allen Klassen bisher durchgehalten und das sind weit mehr als für diese eine SOM.
    Zur eigentlichen Thematik. Die Objekte müssten alle shared sein (Membervariablen) und die Funktionen theoretische alle parallel ausführbar.
    höchstens bei der m_DistFunction->decay() Funktion vermute ich Probleme, weil ich die irgendwo in ein Objekt eingebettet hatte und diese als inline static deklariert ist. Das Obejkt, was halt auch noch andere Funktionen enthalten kann liegt dann in der Klasse für das Netz.


  • Mod

    Was heißt hier, du vermutest da irgendwie irgendwelche Probleme? Du musst doch wissen (nicht vermuten), ob die Sachen threadsicher sind oder nicht, wenn du sie so benutzt! Da wundert mich gar nicht, dass es nicht funktioniert, wenn du dir darüber keine Gedanken gemacht hast. Insbesondere, da du beim Versuch ein Minimalbeispiel zu erstellen ja anscheinend festgestellt hast, dass es nicht an den Indizes, sondern an dieser Funktion liegt (es wäre auch sehr kurios gewesen, wenn es die Indizes gewesen wären).



  • Wenn mich einer vor der Benutzung dieser #pragma-Anweisung gefragt hätte, ob die Methoden threadsicher wären, hätte ich wohl ja gesagt.
    Aber da ich in dem Beispiel das Gegenteil gezeigt habe, ist die Frage eigentlich überflüssig gewesen..
    Wenn ich den Fehler finde, poste ich ihn hier mal rein.



  • ok, es reichte die variablen in der for schleife dort zu deklarieren, weil eine deklaration außerhalb zu doppelzugriffen führt.


Anmelden zum Antworten