Installation von neural-style

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.