Perceptron

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
thomy800
User
Beiträge: 3
Registriert: Samstag 16. Juni 2012, 11:47

Hi. Ich habe nen Code geschrieben, der Perceptronen trainiert und Vorhersagen machen soll, Nur leider kommt da immer totaler Schwachsinn raus und ich kann mir nicht erklären, wo der Fehler liegt. Hier der Code:

Code: Alles auswählen

import sys
import random
import math
import scipy.io as sc
from numpy import *
import matplotlib.pyplot as plt

#x: beispiel
#w: gewichtsvektor
#r: vorhersage {1,-1}
def predict(x, w):
	tw = w.transpose()
	r = dot(tw,x)
	return r
	
def predictSign(x, w):
	r=predict(x,w)
	if r<0: r=-1
	else: r=1
	return r

#x: array mit den einzelnen zu lernenden Beispielen (Vektoren)
#y: array mit den einzelnen Kategorien {1,-1}
#its: anzahl der Iterationen
#rate: Lernrate
#w: gewichtsvektor
def trainperceptron(x, y, its, rate):
	#D: Dimension der Vektoren
	#N: Anzahl der Beispiele
	#w: Gewichtsvektor
	(N,D) = x.shape
	w=zeros((1,D))[0]
	for it in range(0,its):
		d=random.random()
		i=int(math.ceil(d*(N)))-1
		r = predictSign(x[i],w)
		if r!=y[i]:
			w = w + rate/(it+1) * dot(x[i],y[i])
			print w
	return w

x = array([[0.5,0.5],[0.5,-0.5],[-0.5,0.5],[-0.5,-0.5]])
y = array([1,-1,-1,-1])

w = trainperceptron(x, y, 5000, 1.0)
(N,D)=x.shape
for i in range(0,N):
	r = predictSign(x[i],w)
	print str(x[i]) + '=> '+str(r)+'  (' +str(y[i])+')'

print w
Das Problem sollte auf jeden Fall lernbar sein, da das Beispiel des Und-Operators linear separabel ist...
Hat jemand eine Idee?

Grüße,
Thomy800
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Was kommt denn raus und was erwartest Du?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@thomy800: Mal so ein paar allgemeine Anmerkungen zum Quelltext.

Sternchenimporte sollte man vermeiden. Du holst Dir da fast 500 Namen in den Namensraum des Moduls, von denen Du nur ganz wenige wirklich benötigst. Unter anderem überschreibst Du das vorher importierte `random`-Modul aus der Standardbibliothek mit `numpy.random`. Das geht nur deswegen gut, weil das zufällig auch eine `random()`-Funktion enthält. Der Inhalt der beiden Module ist aber nicht identisch. `randrange()` hätte es zum Beispiel nicht gegeben.

Man verliert bei so etwas schnell die Übersicht was eigentlich woher kommt.

`sys`, `scipy`, und `matplotlib` werden anscheinend nicht verwendet.

Der PEP 8 -- Style Guide for Python Code empfiehlt mehr Leerzeichen um Operatoren und nach Kommas, ein anderes Namensschema, und vier Leerzeichen pro Einrückebene. Ausserdem einen Zeilenumbruch nach dem ``:`` bei ``if``/``else``/… auch wenn der Block nur eine Zeile umfasst. Wenn Du Platz sparen möchtest dann vielleicht nicht auf Kosten der Lesbarkeit, sondern in dem nicht jedes kleine Zwischenergebnis an einen eigenen Namen gebunden wird, und durch andere Syntaxkonstrukte. Die ersten beiden Funktionskörper sind im Grunde jeweils Einzeiler.

Quelltext auf Modulebene der nicht zur Definition von Konstanten dient, sollte man vermeiden. Damit kann man sich zu leicht absichtlich oder aus versehen globale Namen einfangen. Du verwendest zum Beispiel sowohl auf Modulebene als auch in Funktionen die Namen `w` und `x`.

Namen dürfen im allgemeinen auch gerne etwas desktiptiver sein als `w` und `x`. Insbesondere wenn man in Kommentaren schreibt wofür die stehen, könnte man sie auch gleich so benennen, dass sich der Kommentar erübrigt. Die Kommentare über Argumente und Rückgabewert würden gute DocStrings abgeben.

Statt `range()` würde ich `xrange()` nehmen wenn ich die Liste nicht wirklich brauche.

Die Berechnung von `i` in `train_perceptron()` ist ziemlich umständlich wenn man bedenkt, dass es im `random`-Modul eine Funktion dafür gibt. Der `zeros()`-Aufruf ist auch umständlicher als er sein müsste. Wieso erzeugst Du da erst ein zweidimensionales Array bei dem eine Dimension 1 ist, nur um da dann ein echtes eindimensionales Array heraus zu holen. Da könnte man auch gleich das erzeugen, was man haben möchte.

Man kann in Python *direkt* über die Elemente von Sequenzen iterieren, ohne den Umweg über einen Index. Wenn man über mehrere Sequenzen parallel iterieren möchte, benutzt man die `zip()`- oder die `itertools.izip()`-Funktion.

Werte mittels `str()` und ``+`` zusammen zu setzen ist eher BASIC als Python. Dafür gibt es Zeichenkettenformatierung mittels ``%``-Operator oder `format()`-Methode auf Zeichenketten.

Da landet man am Ende bei so etwas:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from itertools import izip
from random import randrange
import numpy as np


def predict(example, weights):
    """Returns prediction {1,-1}"""
    return np.dot(weights.transpose(), example)


def predict_sign(example, weights):
    return -1 if predict(example, weights) < 0 else 1


def train_perceptron(examples, categories, iteration_count, rate):
    example_count, dimension = examples.shape
    result = np.zeros(dimension)
    for i in xrange(0, iteration_count):
        j = randrange(example_count)
        example, category = examples[j], categories[j]
        if predict_sign(example, result) != category:
            result += rate / (i + 1) * np.dot(example, category)
            print result
    return result


def main():
    examples = np.array([[0.5, 0.5], [0.5, -0.5], [-0.5, 0.5], [-0.5, -0.5]])
    categories = np.array([1, -1, -1, -1])

    weights = train_perceptron(examples, categories, 5000, 1.0)
    
    for example, category in izip(examples, categories):
        print '%s => %s  (%s)' % (
            example, predict_sign(example, weights), category
        )

    print weights


if __name__ == '__main__':
    main()
thomy800
User
Beiträge: 3
Registriert: Samstag 16. Juni 2012, 11:47

Hi, Danke für die Antworten.
@Hyperion: Also ich habe als Ergebnis genau die Werte erwartet, die in y stehen, da diese gelernt worden sein sollten. Mittlerweile funktioniert das besser. Lustigerweise hatte ich das hier richtig geschrieben, in meinem Code allerdings falsch: die Werte in x waren nicht zentriert, ich hatte {0,1} verwendet anstatt {-0.5,0.5}. Das hatte zur Folge, dass bei einer falschen Antwort beim Lernen eine 0 addiert wurde.

@BlackJack: Du hast voll und ganz recht, ich bin in Python neu, was du da gesehen hast war mein erster Quelltext in Python ;)
Ich kenn das Programmieren bisher nur aus Java, C++ etc. und kenn daher so Feinheiten von Python nicht. Ich war froh, dass es überhaupt erstmal lief.
Ich hatte hier auch nicht den ganzen Quellcode gepostet, daher stehen da noch Libs, die nicht verwendet werden.
Vielen Dank für den Code, sieht echt besser aus :)

Ist es möglich 100% richtige Vorhersagen zu machen, wenn das Problem linear separabel ist und ich die selben Daten für Vorhersagen verwende, die das Programm beim lernen verwendet hat? Weil momentan bekomme ich beim Beispel von oben
nur 75% raus. [0.5,-0.5] (exklusives) oder [-0.5,0.5] wird immer falsch vorhergesagt...

Grüße,
Thomy800
thomy800
User
Beiträge: 3
Registriert: Samstag 16. Juni 2012, 11:47

Code: Alles auswählen

def train_perceptron(examples, categories, iteration_count, rate):
    example_count, dimension = examples.shape
    result = np.zeros(dimension)
    for i in xrange(0, iteration_count):
        j = randrange(example_count)
        example, category = examples[j], categories[j]
        if predict_sign(example, result) != category:
            result += rate / (i + 1) * np.dot(example, category)
            result = result / math.sqrt(np.dot(result,result)))             #Normierung
            print result
    return result
Jo, so funzt es besser ;)
Antworten