Pro:
- Supportet EXTREM viele verschiedene Algorithmen
- Arbeitet nach der Initialisierung recht schnell
- Ist gut parametrisierbar
Cons:
- Nicht für Echtzeitanwendungen anwendbar, Initialisierung dauert ewig (> 200ms)
- CLI über eine Java-Funktion die irgendwann gelöscht werden soll
- Diese Lib geht nur über Windows
- Diese Lib hat noch fast keine Funktionen und keine Parametrisierung
Code: Alles auswählen
;{ Check correct Setup
;Get Current Weka Version
ProgramfilesPath.s = "C:\Program Files\"
d = ExamineDirectory(#PB_Any,ProgramfilesPath,"*.*")
While NextDirectoryEntry(d)
name$ = DirectoryEntryName(d)
If FindString(name$,"Weka")
WekaVersion$ = name$
Break
EndIf
Wend
FinishDirectory(d)
VersionNr$ = RemoveString(ReplaceString(WekaVersion$,"-","."),"Weka.")
If WekaVersion$ = ""
MessageRequester("Error","No Weka found in Programfiles.")
End
EndIf
;Is Weka instlaled?
Global WekaJarPath.s = "C:\Program Files\"+WekaVersion$+"\weka.jar"
If FileSize(WekaJarPath) <= 0
MessageRequester("Error","Cannot load Weka")
End
EndIf
;Is imageFilers installed?
ImageFiltersPackagePath.s = GetHomeDirectory()+"wekafiles\packages\imageFilters\imageFilters.jar"
If FileSize(ImageFiltersPackagePath) <= 0
MessageRequester("Error","Cannot find imageFilter plugin")
EndIf
;Where is Javaw.exe?
Procedure.s Recurvsive_search(Path.s,SearchName.s)
d = ExamineDirectory(#PB_Any,Path,"*.*")
While NextDirectoryEntry(d)
name$ = DirectoryEntryName(d)
If name$ = "." Or name$ = ".."
Continue
EndIf
If DirectoryEntryType(d) = #PB_DirectoryEntry_Directory
Result$ = Recurvsive_search(Path+name$+"\",SearchName)
If Len(Result$) > 0
FinishDirectory(d)
ProcedureReturn Result$
EndIf
ElseIf DirectoryEntryType(d) = #PB_DirectoryEntry_File
If FindString(name$,SearchName) Or name$ = SearchName
FinishDirectory(d)
ProcedureReturn Path+name$
EndIf
EndIf
Wend
FinishDirectory(d)
EndProcedure
Global javapath.s = Recurvsive_search(GetPathPart(WekaJarPath),"java.exe")
If Len(javapath) <= 0
MessageRequester("Error","Could not find javaw.exe in Weka's subdirectories. It should be in C:\Program Files\Weka-3-8-5\jre\zulu11.43.55-ca-fx-jre11.0.9.1-win_x64\bin\java.exe")
End
EndIf
Structure FileAndType
Filepath.s
Class.s ; Only needed for training. For testing, just ignore it.
EndStructure
;}
Procedure.s Weka_Internal_ImageFilter_Extract_Features(ArffFile.s,DatasetPath.s,Filter.s="FCTH") ; TODO: Filter MAGIC HAPPENS HERE!
FeaturesFile.s = GetTemporaryDirectory()+"features.arff"
Select Filter.s
Case "PHOG"
Filter.s = "weka.filters.unsupervised.instance.imagefilter.PHOGFilter"
Case "FCTH"
Filter.s = "weka.filters.unsupervised.instance.imagefilter.FCTHFilter"
EndSelect
;Extract Features
RunProgram(javapath," -cp "+Chr(34)+WekaJarPath+Chr(34)+" weka.Run "+Filter+" -i "+ArffFile+" -D "+DatasetPath+" -o "+FeaturesFile,GetCurrentDirectory(),#PB_Program_Open | #PB_Program_Wait | #PB_Program_Hide)
;Remove first feature-Line 'filename'
RenameFile(FeaturesFile,GetPathPart(FeaturesFile)+"old.arff")
Command.s = "weka.filters.unsupervised.attribute.Remove -R 1"
RunProgram(javapath,"-cp "+Chr(34)+WekaJarPath+Chr(34)+" weka.Run "+command+" -i "+GetPathPart(FeaturesFile)+"old.arff"+" -o "+FeaturesFile,GetCurrentDirectory(),#PB_Program_Open | #PB_Program_Wait | #PB_Program_Hide)
DeleteFile(GetPathPart(FeaturesFile)+"old.arff")
ProcedureReturn FeaturesFile
EndProcedure
Procedure.s Weka_Internal_Create_Imagefilter_Arff(List DataSet.FileAndType())
InternalArffPath.s = GetTemporaryDirectory()+"Purebasic_Weka.arff"
NewMap Classes.s()
ForEach DataSet()
Classes(DataSet()\Class) = ""
Next
arfffile = CreateFile(#PB_Any,InternalArffPath)
WriteStringN(arfffile,"@relation Purebasic_Automation")
WriteStringN(arfffile,"@attribute filename string")
WriteString(arfffile,"@attribute class {")
first = 1
ForEach Classes()
If first = 0
WriteString(arfffile,",")
Else
first = 0
EndIf
WriteString(arfffile,MapKey(Classes()))
Next
FreeMap(Classes())
WriteStringN(arfffile,"}")
WriteStringN(arfffile,"@data")
ForEach DataSet()
WriteStringN(arfffile,DataSet()\Filepath+","+DataSet()\Class)
Next
CloseFile(arfffile)
ProcedureReturn InternalArffPath
EndProcedure
Procedure.s Weka_LoadImages(List DataSet.FileAndType(), DatasetPath.s)
InternalArff$ = Weka_Internal_Create_Imagefilter_Arff(DataSet())
FeatureArff$ = Weka_Internal_ImageFilter_Extract_Features(InternalArff$,DatasetPath,"FCTH")
DeleteFile(InternalArff$)
ProcedureReturn FeatureArff$
EndProcedure
Procedure Weka_LoadText()
EndProcedure
Procedure.s Weka_Train(DatasetArffFile.s, Classifier.s, SaveFileName.s) ; TODO: Training MAGIC HAPPENS HERE!
Select Classifier
Case "SupportVectorMachine"
Command.s = "weka.classifiers.functions.SMO -C 1.0 -L 0.001 -P 1.0E-12 -N 0 -V -1 -W 1 -K "+Chr(34)+"weka.classifiers.functions.supportVector.PolyKernel -E 1.0 -C 250007"+Chr(34)+" -calibrator "+Chr(34)+"weka.classifiers.functions.Logistic -R 1.0E-8 -M -1 -num-decimal-places 4"+Chr(34)
Case "NeuralNetwork"
Command.s = "weka.classifiers.functions.MultilayerPerceptron -L 0.3 -M 0.2 -N 500 -V 0 -S 0 -E 20 -H a"
Case "RandomForest"
Command.s = "weka.classifiers.trees.RandomForest -P 100 -I 100 -num-slots 1 -K 0 -M 1.0 -V 0.001 -S 1"
EndSelect
p = RunProgram(javapath," -cp "+Chr(34)+WekaJarPath+Chr(34)+" weka.Run "+Command+" -t "+DatasetArffFile+" -d "+SaveFileName,GetCurrentDirectory(),#PB_Program_Open | #PB_Program_Hide)
If p
While ProgramRunning(p)
If AvailableProgramOutput(p)
Debug ReadProgramString(p)
EndIf
Wend
EndIf
ProcedureReturn SaveFileName
EndProcedure
Procedure.s Weka_Test(TestDataArff.s, Classifier.s, TrainedModelFile.s)
If FileSize(TrainedModelFile) < 0
MessageRequester("Error","Applied TrainingModel does not exist")
EndIf
Select Classifier
Case "SupportVectorMachine"
Command.s = "weka.classifiers.functions.SMO"
Case "NeuralNetwork"
Command.s = "weka.classifiers.functions.MultilayerPerceptron"
Case "RandomForest"
Command.s = "weka.classifiers.trees.RandomForest"
EndSelect
p= RunProgram(javapath," -cp "+Chr(34)+WekaJarPath+Chr(34)+" weka.Run "+Command+" -T "+TestDataArff+" -l "+TrainedModelFile+" -p 0 ",GetCurrentDirectory(),#PB_Program_Open | #PB_Program_Hide | #PB_Program_Read)
Output$ = ""
If p
While ProgramRunning(p)
If AvailableProgramOutput(p)
Output$ + ReadProgramString(p) + Chr(13)
EndIf
Wend
EndIf
ProcedureReturn Output$
EndProcedure
NewList Datensatz.FileAndType()
Macro AddDatensatz(ClassName,PathName)
AddElement(Datensatz()) : Datensatz()\Class = ClassName : Datensatz()\Filepath = PathName
EndMacro
NewList TestSatz.FileAndType()
CompilerIf Not #PB_Compiler_IsIncludeFile
;Our Data
DatasetPath.s = "C:\Users\"+username()+"\wekafiles\packages\imageFilters\data\butterfly_vs_owl\"
d = ExamineDirectory(#PB_Any,DatasetPath,"*.jpg")
While NextDirectoryEntry(d)
name$ = DirectoryEntryName(d)
If name$ = "." Or name$ = ".."
Continue
EndIf
If FindString(name$,"mno")
AddDatensatz("Butterfly",name$)
ElseIf FindString(name$,"owl")
AddDatensatz("Owl",name$)
EndIf
Wend
FinishDirectory(d)
;Split Test-Datensatz away
OwlCounter = 0
ButterflyCounter = 0
ForEach Datensatz()
If Datensatz()\Class = "Owl" And OwlCounter < 10
OwlCounter +1
ElseIf Datensatz()\Class = "Butterfly" And ButterflyCounter < 10
ButterflyCounter+1
Else
Continue
EndIf
AddElement(TestSatz()) : TestSatz()\Class = Datensatz()\Class : TestSatz()\Filepath = Datensatz()\Filepath : DeleteElement(Datensatz())
Next
;Start Working
SaveFileName.s = DatasetPath+"Trained.brain"
ArffFile$ = Weka_LoadImages(Datensatz(),DatasetPath)
TrainedBrainFile.s = Weka_Train(ArffFile$,"RandomForest",SaveFileName)
DeleteFile(ArffFile$)
TestArffFile$ = Weka_LoadImages(TestSatz(),DatasetPath)
TestResult.s = Weka_Test(TestArffFile$,"RandomForest",TrainedBrainFile)
DeleteFile(TestArffFile$)
Debug TestResult
CompilerEndIf
Installiert WEKA, ich habe hier Version 3.8.5, ist aber auch auf&ab-wärtskompatibel.
Laded imagefilters.zip und extrahiert es nach C:\Users\DEINUSERNAME\wekafiles\packages\
Also so, dass danach alle Daten in C:\Users\User\wekafiles\packages\imageFilters\ liegen.
Wenn das klappt, sollte das o.g. Programm durchstarten.
Hier noch ein paar Hinweise:
Die Lib ist aktuell nur speziell für Bilder da, kann aber problemlos erweitert werden.
Die wichtigsten Schritte sind:
1) Datensatz erzeugen. Das macht die Funtkion Weka_LoadImages(List Datensatz(),DatasetPath)
1. Parameter sollte eine Liste der Struktur FileAndType sein. Das ist einfach ein String mit dem Dateinamen des Bildes und ein String mit dem Label des Bildes. Wenn ihr nicht trainiert sondern testet, dann lasst einfach das Label leer.
2. Parameter ist der absolute Pfad wo eure Bilder liegen. Sie müssen also alle an einem Ort liegen.
Wenn alles passt wird im %Temp%-Ordner eine Purebasic_Weka.arff Datei angelegt.
2) Daten filtern / Merkmale extrahieren
Der o.g. LoadImages Befehl macht automatisch diesen Schritt. Jetzt werden die Bilder von WEKA ausgewertet und die Merkmale extrahiert. Automatisch wird der FCTH-Filter angewandt, ihr könnt aber auch der Funktion Weka_Internal_ImageFilter_Extract_Features ein PHOG-Filter übergeben. Zum Abspeichern der Ergebnisse wird dann die Datei features.arff erzeugt. Die alte
Purebasic_Weka.arff wird gelöscht. Die neue Datei wird nochmal kurz umbenannt, gelöscht und wiedererzeugt. Hat was damit zu tun, dass die extrahierten Merkmale alles nur noch Zahlen sind, und das Label, also der Text des Bildes auch noch in den Daten drin steht. Das wird rausgelöscht.
3) Training
Der Befehl Weka_Train(DatasetArffFile.s, Classifier.s, SaveFileName.s) startet das Training mit der von euch oben angegebenen Arff-Datei (kam als Rückgabeparameter auch zurück) und speichert das Ergebnis in eine Wunschdatei. Ich nenne die zum Spass immer .model oder .brain, ist aber kein geschütztes Format.
Die von mir eingebauten Classifier sind zurzeit :
- "SupportVectorMachine"
- "NeuralNetwork"
- "RandomForest"
4) Test-Datensatz
Wenn nicht bereits geschehen, dann braucht ihr jetzt noch einen Test-Datensatz (muss disjunkt sein) mit dem ihr eure neue AI testen könnt. Disjunkt, für die die es nicht wissen heisst, dass der Datensatz keine gemeinsamen Daten mit dem Training-Satz haben sollte. Das wäre als würde man in der Mathearbeit die gleichen Aufgaben wie in den Hausaufgaben bekommen - das kann man ja eh schon
Zum Erstellen nehmt ihr den Befehl aus Schritt 1: Weka_LoadImages(TestSatz(),DatasetPath)
5) Testen! Yey
Die Funktion Weka_Test(TestArffFile$,"RandomForest",TrainedBrainFile) nimmt die drei Parameter:
String mit dem Pfad auf die Arff-Datei aus Schritt 4
String für den Klassifizierer aus Schritt 3
Vortrainiertes Model aus Schritt 3
Wenn alles klappt bekommt ihr als Rückgabe einen String mit dem Ergebnis drin.
Hier ein Beispiel (auch im Code) zur Erzeugung der Datenbasis:
Code: Alles auswählen
;Our Data
DatasetPath.s = "C:\Users\"+username()+"\wekafiles\packages\imageFilters\data\butterfly_vs_owl\"
d = ExamineDirectory(#PB_Any,DatasetPath,"*.jpg")
While NextDirectoryEntry(d)
name$ = DirectoryEntryName(d)
If name$ = "." Or name$ = ".."
Continue
EndIf
If FindString(name$,"mno")
AddDatensatz("Butterfly",name$)
ElseIf FindString(name$,"owl")
AddDatensatz("Owl",name$)
EndIf
Wend
FinishDirectory(d)
;Split Test-Datensatz away
OwlCounter = 0
ButterflyCounter = 0
ForEach Datensatz()
If Datensatz()\Class = "Owl" And OwlCounter < 10
OwlCounter +1
ElseIf Datensatz()\Class = "Butterfly" And ButterflyCounter < 10
ButterflyCounter+1
Else
Continue
EndIf
AddElement(TestSatz()) : TestSatz()\Class = Datensatz()\Class : TestSatz()\Filepath = Datensatz()\Filepath : DeleteElement(Datensatz())
Next
Code: Alles auswählen
;Start Working
SaveFileName.s = DatasetPath+"Trained.brain"
ArffFile$ = Weka_LoadImages(Datensatz(),DatasetPath)
TrainedBrainFile.s = Weka_Train(ArffFile$,"RandomForest",SaveFileName)
DeleteFile(ArffFile$)
TestArffFile$ = Weka_LoadImages(TestSatz(),DatasetPath)
TestResult.s = Weka_Test(TestArffFile$,"RandomForest",TrainedBrainFile)
DeleteFile(TestArffFile$)
Das liest sich jetzt so, dass die erste Datei laut eigenen Angaben ein "Butterfly" ist und dass das Ergebnis mit einer Sicherheit von 83% das ganze als Butterfly erkannt hat. Im Falle der 4. Datei wurde beispielsweise ein Butterfly mit 55% als "Owl" erkannt - also falsch. Denkt an das Thema "Disjunkt" von oben, sonst steht da überall nur 1.0 und er macht angeblich alles richtig. Hier ist allerdings ein kleiner Fehler passiert: Normalerweise kennt man die Labels der Bilder ja nicht vor dem Test. Daher wäre die erste Spalte eigentlich leer. (Hab ich aus Faulheit aus dem Test-Datensatz rausgeschnitten. Richtig wäre es gewesen, die Class-Information wegzuwerfen - Ist aber für den Test egal weil es ignoriert wird)=== Predictions on test data ===
inst# actual predicted error prediction
1 1:Butterfly 1:Butterfly 0.83
2 1:Butterfly 1:Butterfly 0.63
3 1:Butterfly 1:Butterfly 0.98
4 1:Butterfly 2:Owl + 0.55
5 1:Butterfly 1:Butterfly 0.58
6 1:Butterfly 2:Owl + 0.52
7 1:Butterfly 2:Owl + 0.51
8 1:Butterfly 1:Butterfly 0.74
9 1:Butterfly 1:Butterfly 0.91
10 1:Butterfly 1:Butterfly 0.83
11 2:Owl 2:Owl 0.84
12 2:Owl 2:Owl 0.83
13 2:Owl 2:Owl 0.8
14 2:Owl 2:Owl 0.92
15 2:Owl 2:Owl 0.89
16 2:Owl 2:Owl 0.78
17 2:Owl 2:Owl 0.91
18 2:Owl 2:Owl 0.89
19 2:Owl 2:Owl 0.6
20 2:Owl 2:Owl 0.98
Der hier verwendete Datensatz ist Teil des imagefilters-packets. Siehe: https://github.com/mmayo888/ImageFilter (gleicher Link wie oben)
So und jetzt ihr:
- Soll ich auch ne Demo schreiben, wenn ja wofür? (Captchas, Spiel, Generierung...?)
- Was ist alles unklar? Ist einfach mal so dahingeschrieben weil ich die Lib selbst schon zweimal neu geschrieben hab weil ich es nicht gespeichert hab. Also sorry für nicht schön.