@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()