In dieser Anleitung ist beschrieben, wie man seinen Computer mit Hilfe von „künstlicher Intelligenz“ in einen kleinen van Gogh verwandelt. Als Grundlage dient dabei die Torch Implementierung des „neural-style“ Algorithmus von jcjohnson, welche auf GitHub zu finden ist.
[Not a valid template]
Grundsätzlich stehen am heimischen Arbeitsplatz zwei Wege zur Verfügung um Bilder im „neural-style“ zu generieren. Zum einen besteht die Möglichkeit einen physikalischen Computer zu verwenden, zum anderen kann man das Ganze auch in einer virtuellen Maschine realisieren. Beide Möglichkeiten haben jedoch Vor- und Nachteile, welche man im Vorfeld abwägen sollte.
Der physikalische Weg
+ Nutzung einer CUDA fähigen Nvidia Grafikkarte
+ relativ schnelle Berechnung
– Arbeitsspeicher der Grafikkarte sollte mindestens 2GB betragen
– für Optimierung wird ein zusätzlicher Computer / Notebook benötigt
Der virtuelle Weg
+ Schnell eingerichtet
+ Keine zusätzliche Hardware notwendig
– relativ langsame Berechnung
– extrem hoher Speicherverbrauch
Ich habe meine physikalische Installation auf einer externen USB Festplatte vorgenommen, welche ich bei Bedarf über das Bootmenü starten kann. Es ist auch möglich einen USB-Stick zu verwenden, jedoch ist dies – abhängig von der Geschwindigkeit des Sticks – ein echter Zeitfresser. Zudem ist darauf zu achten, dass der Datenträger mindestens 10GB groß ist. Ich versuchte es auf einem 8GB USB-Stick und kann nur davon abraten.
Schritt 0 – Installation von Ubuntu
Empfohlen ist die Verwendung von Ubuntu Desktop 14.04.04 (64bit). Es ist auch möglich Ubuntu Server zu verwenden, jedoch wird bei der späteren Installation von CUDA die Desktopumgebung für Ubuntu nachinstalliert, sodass man auch gleich die Desktop Variante wählen kann. Man kann natürlich auch ein verwandtes Derivat wie z.B. Xubuntu verwenden, aber das ist vom Prinzip her egal.
Schritt 1 – Anpassung von Ubuntu
Nach der Installation von Ubuntu empfehle ich die zusätzliche Einrichtung von zwei Paketen, welche für den Remotezugriff und zur Analyse der Systemlast hilfreich sind.
Für die Analyse der Systemlast installiert man htop.
sudo apt-get install htop
Für den späteren Zugriff über SSH wird der openssh-server installiert
sudo apt-get install openssh-server sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.factory-defaults sudo chmod a-w /etc/ssh/sshd_config.factory-defaults sudo restart ssh
Anschließend prüft man ob der Dienst läuft
sudo service ssh status
Schritt 2 – Einrichtung von neural-style
Die Anleitung basiert ab diesem Punkt auf der Anleitung von jcjohnson die auf GitHub zu finden ist. Ich gehe daher nicht näher auf die Kommentare zu den Befehlen ein, sondern hake an den Stellen ein, an denen ich auf Probleme gestoßen bin oder an denen ich weitere Hinweise habe. Die Teilschritte 2.1 bis 2.3 sind für die physikalische und die virtuelle Umgebung gleich. Zur Installation öffnet man ein Konsolenfenster in das man die Befehle einzeln eingibt. Ich empfehle zudem ein zweites Konsolenfenster zu öffnen – wofür erkläre ich später. Um die Fenster auseinander zu halten, nenne ich das für die Installation A und das zweite B.
Teilschritt 2.1 – Installation von torch7
cd ~/ curl -s https://raw.githubusercontent.com/torch/ezinstall/master/install-deps | bash git clone https://github.com/torch/distro.git ~/torch --recursive cd ~/torch; ./install.sh source ~/.bashrc
Nach der Installation kann man testen ob alles erfolgreich war indem man th in die Konsole schreibt. Im Idealfall erscheint die nachfolgende Ausgabe. Die Torch Shell kann man mit exit wieder beenden.
michael@box:~/torch$ th ______ __ | Torch7 /_ __/__ ________/ / | Scientific computing for Lua. / / / _ \/ __/ __/ _ \ | Type ? for help /_/ \___/_/ \__/_//_/ | https://github.com/torch | http://torch.ch th>
Teilschritt 2.2 – Installation von loadcaffe
sudo apt-get install libprotobuf-dev protobuf-compiler luarocks install loadcaffe
Teilschritt 2.3 – Installation von neural-style
cd ~/ git clone https://github.com/jcjohnson/neural-style.git cd neural-style sh models/download_models.sh
Bevor man nun neural-style im CPU Modus startet, gibt man den Befehl htop im Konsolenfenster B ein. Man erhält eine Leistungsübersicht des Systems. In meinem Fall verbraucht das System 1,6GB Arbeitsspeicher und die CPU Kerne sind nur minimal belastet. Nun startet man im Konsolenfenster A den neural-style Prozess im CPU Modus.
th neural_style.lua -gpu -1 -print_iter 1
Wenn alles funktioniert ist folgende Ausgabe zu erkennen
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:505] Reading dangerously large protocol message. If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons. To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h. [libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 574671192 Successfully loaded models/VGG_ILSVRC_19_layers.caffemodel conv1_1: 64 3 3 3 conv1_2: 64 64 3 3 conv2_1: 128 64 3 3 conv2_2: 128 128 3 3 conv3_1: 256 128 3 3 conv3_2: 256 256 3 3 conv3_3: 256 256 3 3 conv3_4: 256 256 3 3 conv4_1: 512 256 3 3 conv4_2: 512 512 3 3 conv4_3: 512 512 3 3 conv4_4: 512 512 3 3 conv5_1: 512 512 3 3 conv5_2: 512 512 3 3 conv5_3: 512 512 3 3 conv5_4: 512 512 3 3 fc6: 1 1 25088 4096 fc7: 1 1 4096 4096 fc8: 1 1 4096 1000 WARNING: Skipping content loss Iteration 1 / 1000 Content 1 loss: 2091178.593750 Style 1 loss: 30021.292114 Style 2 loss: 700349.560547 Style 3 loss: 153033.203125 Style 4 loss: 12404635.156250 Style 5 loss: 656.860304 Total loss: 15379874.666090 Iteration 2 / 1000 Content 1 loss: 2091177.343750 Style 1 loss: 30021.292114 Style 2 loss: 700349.560547 Style 3 loss: 153033.203125 Style 4 loss: 12404633.593750 Style 5 loss: 656.860304 Total loss: 15379871.853590
Sieht man jetzt auf das Konsolenfenster B fällt auf, dass Auslastung des Arbeitsspeichers und CPU Kerne enorm angestiegen ist. In meinem Fall sind es rund 5GB Speicher und die CPU Kerne liegen jeweils bei über 90% Last. Für eine Iteration rechnet der Computer in meinem Fall rund 5 Sekunden, was zu einer Gesamtzeit von 83 Minuten führen würde. Mit der Tastenkombination STRG+C bricht man den neural-style Prozess wieder ab.
An dieser Stelle ist leider für alle nicht CUDA Nutzer Schluss. Es gibt die Möglichkeit über Parameter den Speicherverbrauch zu senken, wesentlich schneller wird es jedoch nicht. Ich empfehle die GitHub Seite zu neural-style nach Tipps zu durchsuchen – es gibt viele Anwender die nicht über Grafikkarte rechnen oder eine virtuelle Maschine verwenden – aber so wirklich toll und schnell wird’s meiner Ansicht nach nicht.
Teilschritt 2.4 – Installation von CUDA
wget http://developer.download.nvidia.com/compute/cuda/7_0/Prod/local_installers/rpmdeb/cuda-repo-ubuntu1404-7-0-local_7.0-28_amd64.deb sudo dpkg -i cuda-repo-ubuntu1404-7-0-local_7.0-28_amd64.deb sudo apt-get update sudo apt-get install cuda
Nun ist ein Neustart notwendig um die neuinstallierten Nvidia Treiber zu laden.
sudo shutdown now -r
Nach dem Neustart öffnet man wieder zwei Konsolenfenster und prüft ob alles in Ordnung ist, indem man den Befehl nvidia-smi in eins der beiden Konsolenfenster eingibt. Die angezeigten Daten sind natürlich von Grafikkarte zu Grafikkarte verschieden. Bei einem Test mit einer 2GB Geforce 660TI wurden zum Beispiel auch nicht alle Werte angezeigt.
michael@box:~$ nvidia-smi Sat Mar 12 18:19:31 2016 +------------------------------------------------------+ | NVIDIA-SMI 352.63 Driver Version: 352.63 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 970 Off | 0000:01:00.0 On | N/A | | 38% 37C P8 19W / 250W | 381MiB / 4095MiB | 1% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 1227 G /usr/bin/X 226MiB | | 0 2044 G compiz 137MiB | +-----------------------------------------------------------------------------+
Teilschritt 2.5 – Installation des CUDA backend für torch
cd ~/ cd neural-style luarocks install cutorch luarocks install cunn
Im Konsolenfenster erschienen bei mir an dieser Stelle jede Menge CMake Warnungen, welche aber anscheinend einfach ignoriert werden können. Nach der Installation kann man testen ob alles OK ist.
th -e "require 'cutorch'; require 'cunn'; print(cutorch)"
Die Ausgabe sieht in etwa wie folgt aus
michael@box:~/neural-style$ th -e "require 'cutorch'; require 'cunn'; print(cutorch)" { getPeerToPeerAccess : function: 0x4090b420 getStream : function: 0x4090b0f0 getDeviceCount : function: 0x4090b3c8 setHeapTracking : function: 0x4090b008 setRNGState : function: 0x4090af90 getRNGState : function: 0x4090af68 reserveBlasHandles : function: 0x4090a158 setDefaultStream : function: 0x4090b148 getMemoryUsage : function: 0x4090a180 streamBarrier : function: 0x4090b248 manualSeed : function: 0x4090adb0 synchronize : function: 0x40909cf0 reserveStreams : function: 0x4090ae78 getDevice : function: 0x4090b328 seed : function: 0x4090ad38 deviceReset : function: 0x4090b378 Event : {...} streamWaitForMultiDevice : function: 0x4090b1f8 withDevice : function: 0x41646d00 test : function: 0x41646c98 _stategc : userdata: 0x4091df60 synchronizeAll : function: 0x4090a100 initialSeed : function: 0x4090ad88 getDeviceProperties : function: 0x4090b4d0 manualSeedAll : function: 0x4090af40 getState : function: 0x4090afe0 CudaHostAllocator : torch.Allocator setStream : function: 0x4090af18 streamBarrierMultiDevice : function: 0x4090b2a8 getBlasHandle : function: 0x4090ae28 createCudaHostTensor : function: 0x41646d40 setPeerToPeerAccess : function: 0x4090b478 streamSynchronize : function: 0x4090b300 seedAll : function: 0x4090ad60 setDevice : function: 0x4090a1a8 getNumStreams : function: 0x4090aec8 setBlasHandle : function: 0x40909f18 getNumBlasHandles : function: 0x4090a218 streamWaitFor : function: 0x4090b198 _state : userdata: 0x01751270 }
Jetzt startet man den Test von neural-style mit CUDA in Konsolenfenster A
th neural_style.lua -gpu 0 -print_iter 1
Und sobald die Iterationen laufen, ruft man im Konsolenfenster B den Befehl nvidia-smi auf um die Auslastung der Grafikkarte zu überprüfen.
michael@box:~$ nvidia-smi Sat Mar 12 18:24:18 2016 +------------------------------------------------------+ | NVIDIA-SMI 352.63 Driver Version: 352.63 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 970 Off | 0000:01:00.0 On | N/A | | 56% 58C P2 185W / 250W | 3300MiB / 4095MiB | 97% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 1227 G /usr/bin/X 235MiB | | 0 2044 G compiz 146MiB | | 0 7425 C /home/michael/torch/install/bin/luajit 2900MiB | +-----------------------------------------------------------------------------+
Man sieht in der letzten Zeile im Processes Abschnitt, das der neural-style Prozess jetzt genau 2900 MB Grafikkartenspeicher verwendet. Prüft man nun CPU und RAM mit Hilfe von htop, ist zu erkennen, dass CPU und RAM nicht mehr so stark ausgelastet sind. Wirft man zudem nochmal einen Blick auf die Iterationen, so ist zu erkennen, dass sie wesentlich schneller laufen als auf der CPU. In meinem Fall ein Anstieg von 1 Iteration pro 5 Sekunden auf 10 Iterationen pro 5 Sekunden.
Teilschritt 2.6 – Installation von cuDNN
Um cuDNN nutzen zu können, muss man sich bei Nvidia als Entwickler registrieren. Die Bestätigung des dafür angelegten Nutzerkontos dauerte bei mir etwa einen Tag. Danach konnte ich cudnn bei Nvidia herunterladen und in das neural-style Verzeichnis kopieren.
cd ~/ cd neural-style tar -xzvf cudnn-7.0-linux-x64-v4.0-prod.tgz sudo cp cuda/lib64/libcudnn* /usr/local/cuda-7.0/lib64/ sudo cp cuda/include/cudnn.h /usr/local/cuda-7.0/include/ luarocks install cudnn
Anschließend steht das cudnn Backend zur Verfügung und kann verwendet werden. Es sei denn, man bekommt einen Fehler – so war es bei mir.
michael@box:~/neural-style$ th neural_style.lua -gpu 0 -backend cudnn nil /home/michael/torch/install/bin/luajit: /home/michael/torch/install/share/lua/5.1/trepl/init.lua:384: /home/michael/torch/install/share/lua/5.1/trepl/init.lua:384: /home/michael/torch/install/share/lua/5.1/cudnn/ffi.lua:1279: 'libcudnn (R4) not found in library path. Please install CuDNN from https://developer.nvidia.com/cuDNN Then make sure files named as libcudnn.so.4 or libcudnn.4.dylib are placed in your library load path (for example /usr/local/lib , or manually add a path to LD_LIBRARY_PATH) stack traceback: [C]: in function 'error' /home/michael/torch/install/share/lua/5.1/trepl/init.lua:384: in function 'require' neural_style.lua:64: in function 'main' neural_style.lua:500: in main chunk [C]: in function 'dofile' ...hael/torch/install/lib/luarocks/rocks/trepl/scm-1/bin/th:145: in main chunk [C]: at 0x00406670
Der Fehler gibt an, dass die CUDA Bibliotheken nicht im Pfad sind – sprich nicht gefunden werden. Es ist erforderlich, dass der Pfad zu den Bibliotheken an die Pfadvariable LD_LIBRARY_PATH angehangen wird. Um zu überprüfen wo die benötigten Dateien liegen, kann man danach suchen.
locate libcudnn.so.4
Der Pfad zu den CUDA Bibliotheken lautete bei mir /usr/local/cuda-7.0/lib64/ und ließ sich mit folgendem Befehl an die o.g. Pfadvariable anfügen.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-7.0/lib64
Damit man diesen Befehl nach einem Neustart nicht erneut eingeben muss, kann man ihn an in der .bashrc als letzten Eintrag anfügen. Hierfür ruft man einen Editor für die ~/.bashrc auf
gedit ~/.bashrc
und ergänzt am Ende der Datei den Befehl
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-7.0/lib64
Der Fehler ist behoben und man kann den neural-style Aufruf mit cudnn Parameter verwenden
th neural_style.lua -gpu 0 -backend cudnn
Die Eingabe von nvdia-smi im zweiten Konsolenfenster zeigt nun folgende Werte
michael@box:~$ nvidia-smi Sat Mar 12 18:33:24 2016 +------------------------------------------------------+ | NVIDIA-SMI 352.63 Driver Version: 352.63 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 970 Off | 0000:01:00.0 On | N/A | | 52% 52C P2 158W / 250W | 1928MiB / 4095MiB | 98% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 1227 G /usr/bin/X 238MiB | | 0 2044 G compiz 142MiB | | 0 8169 C /home/michael/torch/install/bin/luajit 1528MiB | +-----------------------------------------------------------------------------+
Der Speicherverbrauch des neural-style Prozesses ist durch die Verwendung von cudnn von 2900MB auf 1528MB gesunken.
Schritt 3 – Optimierung
Durch die Nutzung von cudnn ist der Speicherverbrauch so tief gesunken, dass es nun möglich ist größere Bilder zu verarbeiten. Ich habe ermittelt, dass sowohl style als auch content Bild maximal 900×665 Pixel groß sein dürfen – besser gesagt das Produkt sollte 589500 Pixel nicht übersteigen. Ohne Optimierung kam ich beim Testen nur auf 1024×512 Pixel (als Produkt 524288 Pixel). Allerdings benötigt man auch den Speicher der Grafikkarte, welcher vom Displaymanager verwendet wird. Im oberen Auszug ist zu erkennen, dass dies satte 380MB sind. Es ist also notwendig den Displaymanger Dienst zu beenden.
ACHTUNG: Da man an dem System nicht mehr grafisch arbeiten kann sobald der Displaymanager beendet wurde, ist es ratsam bereits an dieser Stelle passende style und content Bilder in das neural-style Verzeichnis zu legen! Man kann sie auch später mit sftp o.ä. dort hinkopieren, aber solang man „Klickibunti“ hat, nutzt man doch lieber das :-)
An dieser Stelle kommt das bei den Nachteilen genannte zusätzliche Gerät zum Einsatz. In meinem Fall ein kleines Netbook auf dem Xubuntu läuft. Das zweite Gerät verwendet man um über ssh auf dem Hauptcomputer zu arbeiten, da man, wie bereits erwähnt, nichts mehr sieht sobald der Displaymanager beendet ist.
ssh <IP-Adresse-des-Hauptrechners>
Nachdem man sich angemeldet hat, kann man ruhigen Gewissens den Displaymanager beenden, ohne die Kontrolle zu verlieren.
sudo service lightdm stop
Prüft man nun mit nvidia-smi den Grafikkartenspeicher, so fällt auf, dass gerade mal nur noch 15MB Grafikkartenspeicher in Verwendung sind.
michael@box:~$ nvidia-smi Sat Mar 12 18:59:19 2016 +------------------------------------------------------+ | NVIDIA-SMI 352.63 Driver Version: 352.63 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 970 Off | 0000:01:00.0 Off | N/A | | 0% 35C P0 43W / 250W | 15MiB / 4095MiB | 0% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | No running processes found | +-----------------------------------------------------------------------------+
Jetzt navigiert man zum neural-style Verzeichnis
cd ~/ cd neural-style
und kann einen Test mit eigenen Bildern machen. In meinem Fall sind es zwei PNG Dateien mit einer Auflösung von je 900×665 Pixeln.
th neural_style.lua -style_image simg.png -content_image cimg.png -output_image oimg.png -gpu 0 -print_iter 10 -save_iter 1000 -image_size 900 -backend cudnn -cudnn_autotune -optimizer lbfgs
Nachdem die Iterationen begonnen haben, kann man über ein zweites Konsolenfenster eine weitere SSH Sitzung zum Hauptrechner aufbauen und dort mit nvidia-smi den Speicherverbrauch der Grafikkarte prüfen.
michael@box:~$ nvidia-smi Sat Mar 12 19:02:19 2016 +------------------------------------------------------+ | NVIDIA-SMI 352.63 Driver Version: 352.63 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | |===============================+======================+======================| | 0 GeForce GTX 970 Off | 0000:01:00.0 Off | N/A | | 55% 56C P2 180W / 250W | 4087MiB / 4095MiB | 99% Default | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: GPU Memory | | GPU PID Type Process name Usage | |=============================================================================| | 0 2408 C /home/michael/torch/install/bin/luajit 4070MiB | +-----------------------------------------------------------------------------+
In meinem Fall sind sind gerade noch 8MB übrig. Der neural-style Prozess verbraucht exakt 4070MB.