2010-02-10 16 views
24

Ich schreibe ein ziemlich kompliziertes maschinelles Lernprogramm für meine Diplomarbeit in Computer Vision. Es funktioniert ziemlich gut, aber ich muss weiterhin neue Dinge ausprobieren und neue Funktionen hinzufügen. Das ist problematisch, weil ich manchmal Fehler einführe, wenn ich den Code erweitere oder versuche, einen Algorithmus zu vereinfachen.Unit Testing Machine Lerncode

Natürlich ist es das Richtige, Unit Tests hinzuzufügen, aber es ist nicht klar, wie dies zu tun ist. Viele Komponenten meines Programms erzeugen eine etwas subjektive Antwort, und ich kann keine Gesundheitsüberprüfungen automatisieren.

Zum Beispiel hatte ich einen Code, der eine Kurve mit einer niedriger auflösenden Kurve approximierte, so dass ich rechenintensive Arbeit an der niedriger auflösenden Kurve machen konnte. Ich habe versehentlich einen Fehler in diesen Code eingeführt und ihn nur durch eine sorgfältige Suche gefunden, als die Ergebnisse meines gesamten Programms etwas schlechter wurden.

Aber als ich versuchte, einen Unit-Test dafür zu schreiben, war unklar, was ich tun sollte. Wenn ich eine einfache Kurve mache, die eine klar korrekte Version mit niedrigerer Auflösung hat, dann teste ich nicht wirklich alles aus, was schief gehen könnte. Wenn ich eine einfache Kurve mache und dann die Punkte ein wenig störe, beginnt mein Code, verschiedene Antworten zu erzeugen, obwohl dieses spezielle Stück Code jetzt wirklich gut zu funktionieren scheint.

Antwort

8

Sie mögen die Ironie vielleicht nicht schätzen, aber im Grunde ist das, was Sie dort haben, Legacy-Code: ein Stück Software ohne Unit-Tests. Natürlich wissen Sie nicht, wo Sie anfangen sollen. Daher ist es hilfreich, wenn Sie sich mit dem Umgang mit Legacy-Code befassen.

Der definitive Gedanke dazu ist Michael Feathers Buch, Effektiv mit Legacy Code arbeiten. Früher gab es eine hilfreiche Zusammenfassung davon auf der ObjectMentor-Site, aber leider ist die Website den Weg des Unternehmens gegangen. WELC hat jedoch in Berichten und anderen Artikeln ein Vermächtnis hinterlassen. Check them out (or just buy the book), obwohl die wichtigsten Lektionen diejenigen sind, die S.Lott und Tvanfosson in ihren Antworten abdecken.

+0

Dies ist eigentlich der hilfreichste Rat. All mein erfolgreiches Debugging ist auf die Verwendung solcher Techniken von Hand zurückzuführen. Aber dieses PDF gibt einige gute Ratschläge zur Automatisierung des Prozesses. Ihr PDF-Link funktionierte nicht für mich, aber ein einfacher Google fand es. – forefinger

+0

@forefinger - Ich habe jetzt den Link behoben. Aber ich bin froh, dass Sie den Artikel gefunden haben und ihn nützlich fanden. – APC

+2

Der Autor dieses PDFs hat jetzt ein exzellentes Buch zum selben Thema: http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 – TrueWill

11

Ohne Ihren Code zu sehen, ist es schwer zu sagen, aber ich vermute, dass Sie versuchen, Tests auf einem zu hohen Niveau zu schreiben. Vielleicht möchten Sie darüber nachdenken, Ihre Methoden in kleinere deterministische Komponenten zu zerlegen und diese zu testen. Testen Sie dann die Methoden, die diese Methoden verwenden, indem Sie Scheinimplementierungen bereitstellen, die vorhersagbare Werte von den zugrunde liegenden Methoden zurückgeben (die sich wahrscheinlich an einem anderen Objekt befinden). Dann können Sie Tests schreiben, die die Domäne der verschiedenen Methoden abdecken und sicherstellen, dass Sie die gesamte Bandbreite möglicher Ergebnisse abdecken. Für die kleinen Methoden tun Sie dies, indem Sie Werte bereitstellen, die die Domäne der Eingaben darstellen. Für die Methoden, die davon abhängen, stellen Sie Mock-Implementierungen bereit, die den Bereich der Ergebnisse aus den Abhängigkeiten zurückgeben.

+0

Dieser Hinweis war hilfreich. Im Beispielfall mache ich die Approximation über ein dynamisches Programm. Dies kann in mehrere Komponenten zerlegt werden, die deterministisch sind: 1. Berechnen des Fehlers eines bestimmten Teils der Approximation. Ich kann das einfach per Hand für eine bestimmte Kurve machen. 2. Sicherstellen, dass die allgemeine Zielfunktion korrekt ist. Wieder kann ich es mit der Hand machen. 3. Stellen Sie sicher, dass das dynamische Programm korrekt ist. (Hier war der Fehler.) – forefinger

+1

Wenn ich weiß, dass die Zielfunktion korrekt ist, kann ich dies testen, indem ich einfach zerlegbare Kurven einspeise, die gestört wurden. Solange die Antwort mir bessere Ergebnisse liefert als die Antwort, die ich dachte, wollte ich, das dynamische Programm funktioniert wahrscheinlich richtig. – forefinger

11

"Dann teste ich nicht wirklich alles aus, was schiefgehen könnte."

Korrekt.

Der Job der Komponententests ist nicht zu testen alles das könnte schief gehen.

Die Aufgabe von Komponententests ist zu testen, dass was Sie haben, die richtige Sache, bestimmte Eingaben und spezifische erwartete Ergebnisse gegeben. Der wichtige Teil hier ist die spezifische sichtbare, externe Anforderungen werden durch spezifische Testfälle erfüllt. Nicht dass jede mögliche Sache, die schiefgehen könnte, irgendwie verhindert wird.

Nichts kann Test alles, was schief gehen könnte. Sie können einen Proof schreiben, aber Sie werden es schwer haben, Tests für alles zu schreiben.

Wählen Sie Ihre Testfälle mit Bedacht.

Außerdem besteht die Aufgabe von Komponententests darin, zu testen, dass jeder kleine Teil der gesamten Anwendung das Richtige tut - isoliert.

Ihr "Code, der eine Kurve mit einer niedriger aufgelösten Kurve approximierte", hat wahrscheinlich mehrere kleine Teile, die als separate Einheiten getestet werden können. Isoliert. Das integrierte Ganze könnte auch getestet werden, um sicherzustellen, dass es funktioniert.

Ihre "rechenintensive Arbeit an der niedriger auflösenden Kurve" zum Beispiel hat wahrscheinlich mehrere kleine Teile, die als separate Einheiten getestet werden können. Isoliert.

Dieser Punkt der Komponententests besteht darin, kleine, korrekte Einheiten zu erstellen, die später zusammengebaut werden.

+0

Das scheint ein vernünftiger Ratschlag zu sein, hilft aber nicht so sehr bei meinen spezifischen Problemen, wie bei einigen anderen Antworten. – forefinger

+0

"spezifische Probleme"? Das ist nicht leicht zu verstehen, da Ihre Frage keine spezifischen Probleme aufzulisten scheint. Fühlen Sie sich frei, Ihre Frage zu aktualisieren, wenn Sie weitere Informationen wünschen. –

1

Im Allgemeinen, für statistische Messungen würden Sie ein Epsilon für Ihre Antwort einbauen. I.E. Der mittlere quadratische Unterschied Ihrer Punkte wäre < 0,01 oder einige solcher. Eine andere Möglichkeit besteht darin, mehrmals zu laufen und wenn es "zu oft" fehlschlägt, dann haben Sie ein Problem.

6

Ihre Komponententests müssen eine Art von Fuzz-Faktor verwenden, entweder durch die Annahme von Näherungen oder durch eine Art von probabilistischen Prüfungen.

Wenn Sie beispielsweise eine Funktion haben, die ein Gleitkommaergebnis zurückgibt, ist es fast unmöglich, einen Test zu schreiben, der auf allen Plattformen korrekt funktioniert. Ihre Checks müssten die Approximation durchführen.

TEST_ALMOST_EQ(result, 4.0); 

Above TEST_ALMOST_EQ könnte überprüfen, ob result zwischen 3,9 und 4,1 (zum Beispiel) ist.

Wenn Ihre maschinellen Lernalgorithmen probabilistisch sind, müssen Ihre Tests dies berücksichtigen, indem Sie den Durchschnitt mehrerer Läufe nehmen und erwarten, dass er in einem bestimmten Bereich liegt.

x = 0; 
for (100 times) { 
    x += result_probabilistic_test(); 
} 

avg = x/100; 
TEST_RANGE(avg, 10.0, 15.0); 

Ofcourse, die Tests sind nicht deterministisch, so dass Sie zu stimmen brauchen sie, so dass Sie nicht-flockige Tests mit hohen Wahrscheinlichkeit zu bekommen. (Z. B. erhöhen Sie die Anzahl der Versuche, oder erhöhen Sie die Fehlerspanne).

Sie können auch Mocks für diesen Zweck verwenden (z. B. einen Pseudozufallszahlengenerator für Ihre probabilistischen Algorithmen), und sie helfen in der Regel beim deterministischen Testen bestimmter Codepfade, aber sie sind sehr aufwändig. Idealerweise würden Sie eine Kombination von Fuzzy-Test und Mocks verwenden.

HTH.

+0

Dies ist ein guter Rat, aber es löst meine Probleme nicht wirklich, weil viele der Dinge, die ich überprüfen muss, diskret sind, so dass Fehler für sie nicht berechnet werden können und sie nicht sinnvoll gemittelt werden können. – forefinger

0
  1. Holen Sie sich einen geeigneten Testdatensatz (vielleicht eine Teilmenge von dem, was in der Regel Ihre Verwendung)
  2. einige metrische Berechnen auf diesem Datensatz (zdie Genauigkeit)
  3. Notieren Sie den erhaltenen Wert (kreuzvalidierte)
  4. Dies sollte einen Hinweis darauf geben, was für die Schwelle setzen

Natürlich, wenn das sein kann, wenn Änderungen an Ihrem Code machen die Die Leistung des Datasets wird ein wenig zunehmen, aber wenn sie jemals stark abnimmt, wäre dies ein Anzeichen dafür, dass etwas schief läuft.