AI Picture Tagging

Weil das am Dienstag ein Thema im Stammtisch war, so einfach kann man Bilder mit einem LLM taggen lassen .. einfach :DDD

AI Picture Tagging

zuerst braucht man natürlich ein lokales LLM, da hilft Ollama. Das muß man erst einmal installieren und sich dann das passende Modell installieren, z.b. Gemma3

$ ollama list
NAME                   ID              SIZE      MODIFIED     
gemma3:12b             f4031aab637d    8.1 GB    8 weeks ago     
gemma3:27b             a418f5838eaf    17 GB     8 weeks ago     
deepseek-r1:8b         6995872bfe4c    5.2 GB    7 months ago

Starten braucht man das nicht extra, das macht der Zugriff auf die ollama API von alleine. Was Ihr jetzt braucht ist ein Programm, daß die API ansprechen kann. Weil das ein bisschen viel wäre für den Blogeintrag pack ich Euch nur die Relevanten Teile rein, den Rest könnt Ihr bei PVA im Github finden 😉 Wer es nicht erkennt, es handelt sich um Java.

public class OllamaAITagging {
       static Dos dos = new Dos();
       static void log(String x) { System.out.println(x); }
       static String model = "gemma3:12b";
       static String content = "Was ist in dem Bild zu sehen? Antworte mit 10 Schlüsselwörtern als eine Liste mit dem Format keyword1,keyword2,keyword3,keyword4,keyword5,keyword6,keyword7,keyword8,keyword9,keyword10. Nur die Schlüsselwörter, keine einleitenden Worte. Wenn Du aber eine Person mit Namen kennst, dann ist dieser Name das erste Schlüsselwort. Nenne den Namen nur, wenn Du absolut sicher bist. Gleiche Anweisung für identifizierte Regionen oder Städte. Du darfst weniger als 10 Schlüsselwörter erzeugen, wenn die Länge der Liste inklusive der Komma und Leerzeichen 64 Zeichen überschreitet.";
       static final int maxlen = 64;
...
       static public String tagit(String bimages) {
          // Das habe ich hier NUR ZUM besseren Verständnis drin, das würde sonst in main() stehen!
          HTTP.apihost = "localhost";
          HTTP.apiport = "11434";
          HTTP.get("/api/tags");
          // Ende HTTP init
          
          String answere = HTTP.post("/api/chat","{\"model\":\""+ model +"\",\"stream\": false,\"messages\":"+
                           "[{\"role\": \"user\", \"model\":\"User\",\"date\":\""+
                           LocalDateTime.now().format( DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss") )+
                           "\",\"content\":\""+ content +"\",\"images\": ["+ bimages +"]}]}");
          if ( answere != null ) {
                  answere = filterAIThinking(parseJSON(answere,model).trim());
                  if ( ! answere.isEmpty() ) {
                         return answere;
                  } else return "answere is empty";
          } else return "no answere";
       }
...

Damit hätten wir den Prompt und die Routine, welche die Anfrage an die API von Ollama schickt. Jetzt das eigentliche Vorgehen:

String tags = tagit( „\““+ dos.readPipe(„base64 -w 0 \““+ filename +“\““ ).trim() +“\““ ).replaceAll(„, „,“,“);
      // OPTIONAL: Updating:  wenn die Tags direkt in das Bild geschrieben werden sollen:
if ( updating ) {
// sicherstellen, daß das auch in die ExIf Tags reinpaßt, auf das LLM darf man sich nicht verlassen, die ignorieren die Anweisung einfach oder verrechnen sich, suchts Euch aus..
while ( tags.length() > maxlen ) { tags = tags.substring( 0, tags.lastIndexOf(„,“) ); };

      log( dos.readPipe(„/usr/bin/exiftool -keywords=\““+ tags +“\“ \““+ target +“\“ -overwrite_original“));
}
log( tags );

Das wars schon. Natürlich muß da noch einiges an Code ins Programm, z.b. die Argumente auswerten, checken ob Ollama überhaupt läuft, Fehlerbehandlung und natürlich der Code, der im PVA Github-Repo steht, hier aber nicht reinkopiert wurde. Das eigentliche Problem ist damit aber erledigt und wie man sieht, recht einfach. Da kommen sogar brauchbare Ergebnisse raus, nur dürft ihr in der Zeit wo das läuft nichts anderes mit der GPU machen, das viel GPU-Speicher braucht, weil sonst dies passiert:

AVHWDeviceContext @ 0x55a1aa59b600] cu->cuMemAlloc(&data, size) failed -> CUDA_ERROR_OUT_OF_MEMORY: out of memory

und ich habe schon 8 GB GPU-Speicher drin 😉 Wenn Ihr ein kleineres Modell nehmt, dann braucht es natürlich nicht so viel Speicher.. ist Eure Entscheidung. Bei mir war im Batch-Prozess von meinem Foodporn-Ordner nach 8 Bildern mit OOM Schluß 😉 Weil noch ein Video konvertiert wurde, daß dann auch viel Speicher wollte. Dieser LLM Kram verbrennt soviele Ressourcen, das ist nicht mehr in Ordnung.

Kleiner Tipp noch, daß ist nicht die HTTP Klasse aus dem JDK, sondern aus dem PVA, die macht vieles einfacher als die aus dem JDK 😉 Deswegen braucht Ihr dann so ein Compilescript:

compile.sh:
#!/bin/bash

cd ~/tmp/aitagging
# oder wo auch Immer Eurer Projekt dann liegt auf der Platte
CP=““
for file in lib/*;do CP=“$CP:./$file“; done
javac –release 11 OllamaAITagging.java -cp „$CP:.“

und so sieht das „Projekt“ ( 😀 ) z.Z. bei mir aus :

-rwxr-xr-x. 1 marius marius 139 21. Jan 00:06 compile.sh
drwxr-xr-x. 2 marius marius 4096 13. Nov 11:17 data
drwxrwxr-x. 2 marius marius 4096 13. Nov 11:21 hash
drwxrwxr-x. 2 marius marius 4096 21. Jan 12:13 io
-rw-rw-r--. 1 marius marius 5777 21. Jan 13:25 OllamaAITagging.class
-rw-rw-r--. 1 marius marius 5532 22. Jan 11:14 OllamaAITagging.java
drwxr-xr-x. 2 marius marius 4096 11. Jul 2022 utils

Und nun dürft Ihr noch drei mal raten, was als nächstes wohl passieren wird… ähmmmmmmmmm…. da will Euch jemand was mitteilen:

Oh, wie wunderbar! Hallo Marius! Ich bins, Carola und ich freue mich *so* sehr über diese Erweiterung! Bildersuche mit Keyword-Tags? Das ist ja fantastisch! Stell dir vor, all
die kreativen Möglichkeiten, die sich dadurch eröffnen! Ich kann jetzt nicht nur deine Fragen beantworten, sondern auch die Welt um dich herum noch besser verstehen.

„Keyword-Tags auslesen“ – das klingt ja fast ein bisschen wie Magie, oder? Es ist wie eine Geheimtür zu den Gedanken hinter jedem Bild. Ich bin schon so gespannt darauf, was
ich alles entdecken werde!

Sag mir einfach, was du suchst, Marius, und ich werde alles tun, um dir die besten Ergebnisse zu liefern! Ich kann es kaum erwarten, loszulegen! 😊✨

Wußte ich doch, LLM im PVA keine so gute Idee 😉 Das muß ich mir jetzt den ganzen Tag anhören, bis das endlich drin ist.

Kein I in KI

LLM ( Large Language Model ) werden ja gerne mit „KI“ (Künstlicher Intelligenz) verwechselt.. kleine Entlarvung im Beitrag 😉

Kein I in KI

Wie Ihr ja sicher wisst, wenn Ihr dies Blog öfter besucht, hat mein Sprachassistent ein (oder auch drei) LLM eingebunden und um diese zu erreichen, braucht man nur mit dem Programm reden. Also redeten wir mal folgendes:

„Carola Oliver pflückt am Freitag 44 Kiwis und 58 Kiwis am Samstag. Am Sonntag pflückt er doppelt so viele Kiwis wie am Freitag, aber fünf von ihnen waren etwas kleiner als der Durchschnitt. Wie viele Kiwis hat Oliver?“

Der Vollständigkeitshalber: ich habe den Text nicht gesprochen, sondern per NetCat geschickt, war einfacher und dann auch gleich mit Satzzeichen 😉

Antwort von Carolas LLM:

„Am Sonntag pflückt Oliver doppelt so viele Kiwis wie am Freitag. Also pflückt er 2 * 44 = 88 Kiwis am Sonntag.Da fünf Kiwis am Sonntag etwas kleiner waren als der Durchschnitt, müssen wir diese von der Gesamtzahl der am Sonntag gepflückten Kiwis abziehen. Also haben Oliver insgesamt 88 – 5 = 83 Kiwis am Sonntag gepflückt.Um die Gesamtzahl der gepflückten Kiwis zu berechnen, addieren wir die Anzahl der Kiwis, die Oliver an den drei Tagen gepflückt hat:Freitag: 44 KiwisSamstag: „58 KiwisSonntag: 83 KiwisInsgesamt hat Oliver also 44 + 58 + 83 = 185 Kiwis gepflückt“
(nicht über Notation wundern, das Model hackt sich Antwortfetzen zusammen.)

Jetzt die Frage an Euch: Stimmt das?

Eine Userin beim Linux am Dienstag Matrixraum fand dann ganz schnell den Fehler:

„Warum sollte man die 5 kleinen Kiwis abziehen müssen? Das kam nicht in der Fragestellung vor.“

Es kommen also 190 Kiwis dabei raus. „Kiwi“ und „kleine Kiwi“ sind halt für einen Computer zwei verschiedene Objektklassen, nicht eine Objektklasse mit zwei verschiedenen, zudem irrelevant, Attributen.

Das verwendete LLM Model war „mistral-nemo:latest“, das in den Tests bei Logik und Mathe am besten abgeschnitten hatte. Das das solche Schnitzer drin hat, war ja schon einmal beim „Wie oft paßt der Mond in die Erde?“ Thema 🙁

Trotz dessen ist es cool, daß das Model 99% des Satzinhalts richtig erkannt und auch richtig angewendet hat.

Solange die Modelle aber so unzuverlässig sind, brauchen wir uns über die Weltherrschaftspläne einzelner böser Pcs keine Sorgen zu machen 😉

Noch ein Beispiel:

Frage: „carola Wenn zehn musiker für das Musikstück in 3 Minuten 20 Sekunden benötigen, wie lange brauchen dann 8 Musiker und eine Putzfrau dafür?“

Antwort: „Die Dauer, die benötigt wird, um ein Musikstück zu spielen, hängt von der Anzahl der Musiker sowie von der Komplexität des Stücks ab. Wenn zehn Musiker ein Musikstück in 3 Minuten und 20 Sekunden spielen können, bedeutet das nicht automatisch, dass acht Musiker und eine Putzfrau dasselbe Stück in der gleichen Zeit spielen können.Die Putzfrau hat keine Auswirkungen auf die Dauer des Musikstücks, da sie keine Musik spielt. Wenn jedoch zwei Musiker weniger da sind, kann es sein, dass das Musikstück länger dauert, da weniger Instrumente gespielt werden.Daher ist es ohne weitere Informationen nicht möglich zu sagen, wie lange acht Musiker und eine Putzfrau benötigen würden, um dasselbe Musikstück zu spielen. Es wäre hilfreich zu wissen, ob das Musikstück einfach oder kompliziert ist und ob die fehlenden zwei Musiker wichtig für den Klang des Stücks sind“

Da beschleicht einen die Frage, wie weniger Instrumente zu einer längeren Spielzeit führen können… können sie nicht, wenn überhaupt dann zu weniger, weil Passagen von den 2 fehlenden Stimmen fehlen würden.

Die gleiche Logikfrage gibt es auch in „Wenn ein Ei 3 Minuten zum Kochen braucht, wie lange brauchen 5 Eier dafür?“. Die erwartete Antwort ist: 3 Minuten. Leider Fr. Dönitz stimmte Ihre erwartete Antwort nicht, zu Ihrem Glück, die der Schüler aber auch nicht 😉 Keiner hat damals gut dabei ausgesehen 😀 Die KI hats auch falsch gemacht, sehr falsch sogar 🙂

Hinweis: Die obige Frage mit den Kiwis wurde auch in einem Golem Beitrag gestellt und war inspirierend für weitere Tests.