Es gibt viele Arten von Deep Learning-Frameworks wie PyTorch, Tensorflow und Keras. Dieses Mal werde ich auf ** PyTorch ** achten, das ich oft benutze!
Wussten Sie, dass die C ++ - Version sowie die PyTorch- und Python-Version veröffentlicht wurden? Dies erleichtert die Integration, wenn Sie Deep Learning als Teil der Verarbeitung Ihres C ++ - Programms verwenden möchten!
Bei einer solchen C ++ - Version von PyTorch habe ich mich gefragt ** "C ++ ist eine kompilierte Sprache, also ist sie vielleicht schneller als die Python-Version?" **.
Diesmal habe ich also untersucht ** "Wie viel Geschwindigkeit unterscheidet sich zwischen C ++ und Python?" </ Font> **! Außerdem machte ich mir Sorgen um die Genauigkeit und überprüfte sie.
Dieses Mal werden wir, wie der Titel schon sagt, die C ++ - Version von "PyTorch" verwenden. Sie können es von der folgenden Website herunterladen, versuchen Sie es also bitte!
PyTorch Official: https://pytorch.org/
Ich habe es mit den obigen Einstellungen heruntergeladen. Die "Preview (Nightly) version" enthält immer die neuesten Dateien. Es befindet sich jedoch noch in der Entwicklung. Wenn Sie also die stabile Version verwenden möchten, wählen Sie "Stabil (1.4)".
Außerdem ist "Diesen Befehl ausführen" unten sehr wichtig. Wenn Sie eine Build-Version von CXX 11 oder höher haben, empfehlen wir Ihnen, die untere Version auszuwählen. Derzeit ist es fast CXX17, also denke ich, dass es unten in Ordnung ist. Wenn Sie das oben Gesagte auswählen, treten Verknüpfungsfehler anderer Bibliotheken auf und es wird viel Ärger geben.
Dieses Mal verwenden wir ** Faltungs-Autoencoder </ font> ** (Faltungs-Autoencoder). Verfügbar von meinem GitHub → https://github.com/koba-jon/pytorch_cpp
Dieses Modell ordnet ** Eingabebild (hohe Dimension) ** dem ** latenten Raum (niedrige Dimension) ** zu und diesmal basierend auf dieser ** latenten Variablen (niedrige Dimension) ** ** Bild ( Der Zweck besteht darin, hochdimensional zu erzeugen ** und den Fehler zwischen diesem und dem Eingabebild zu minimieren. Nach dem Training kann dieses Modell wieder ein hochdimensionales Bild aus einem hochdimensionalen Bild durch einen niedrigdimensionalen Raum erzeugen, so dass ** ein latenter Raum erhalten werden kann, der das Trainingsbild ** charakterisiert **. Mit anderen Worten, es hat die Rolle der Dimensionskomprimierung und kann als sogenannte nichtlineare Hauptkomponentenanalyse bezeichnet werden. Dies ist sehr praktisch, da es verschiedene Verwendungszwecke hat, wie z. B. ** Beseitigung des Dimensionsfluchs **, ** Transferlernen ** und ** Erkennung von Anomalien **.
Jetzt werde ich die Struktur des zu verwendenden Modells erläutern.
In Erwartung dieser Effekte haben wir das folgende Netzwerk aufgebaut.
Operation | Kernel Size | Stride | Padding | Bias | Feature Map | BN | Activation | ||
---|---|---|---|---|---|---|---|---|---|
Input | Output | ||||||||
1 | Convolution | 4 | 2 | 1 | False | 3 | 64 | ReLU | 2 | 64 | 128 | True | ReLU |
3 | 128 | 256 | True | ReLU | |||||
4 | 256 | 512 | True | ReLU | |||||
5 | 512 | 512 | True | ReLU | |||||
6 | 512 | 512 | |||||||
7 | Transposed Convolution | 512 | 512 | True | ReLU | ||||
8 | 512 | 512 | True | ReLU | |||||
9 | 512 | 256 | True | ReLU | |||||
10 | 256 | 128 | True | ReLU | |||||
11 | 128 | 64 | True | ReLU | |||||
12 | 64 | 3 | tanh |
Dieses Mal verwenden wir den CelebA-Datensatz, eine Sammlung von 202.599 Promi-Gesichtsbildern (Farbe). Die Bildgröße beträgt 178 x 218 [Pixel], was beim umgekehrten Falten einige Unannehmlichkeiten verursacht. Deshalb habe ich diesmal die Größe auf ** 64 x 64 [Pixel] ** geändert. Von diesen wurden ** 90% (182.340) zum Lernen von Bildern ** und ** 10% (20.259) für Testbilder ** verwendet.
Wenn dies in das vorherige Modell eingegeben wird, wird der latente Raum (C, H, W) = (512,1,1). Wenn Sie ein Bild mit 128 x 128 [Pixel] oder mehr eingeben, wird die Zwischenebene zu einem räumlichen latenten Raum.
Dieses Mal werde ich hauptsächlich untersuchen, ** "wie unterschiedlich die Geschwindigkeit zwischen C ++ und Python ist" **, aber ich möchte die Geschwindigkeit und Leistung unter den folgenden 5 Arten von Umgebungen vergleichen. ..
Wie aus den obigen Funktionen hervorgeht, ist die GPU in Deep Learning, das Bilder verarbeitet, hinsichtlich der Berechnungsgeschwindigkeit überwältigend vorteilhaft.
CPU.py
device = torch.device('cpu') #CPU verwenden
model.to(device) #Modell in CPU verschieben
image = image.to(device) #Daten in die CPU verschieben
GPU.py
device = torch.device('cuda') #Verwenden Sie die Standard-GPU
device = torch.device('cuda:0') #Verwenden Sie die erste GPU
device = torch.device('cuda:1') #Verwenden Sie die zweite GPU
model.to(device) #Modell auf GPU verschieben
image = image.to(device) #Verschieben Sie Daten auf die GPU
CPU.cpp
torch::Device device(torch::kCPU); //CPU verwenden
model->to(device); //Modell in CPU verschieben
image = image.to(device); //Daten in die CPU verschieben
GPU.cpp
torch::Device device(torch::kCUDA); //Verwenden Sie die Standard-GPU
torch::Device device(torch::kCUDA, 0); //Verwenden Sie die erste GPU
torch::Device device(torch::kCUDA, 1); //Verwenden Sie die zweite GPU
model->to(device); //Modell auf GPU verschieben
image = image.to(device); //Verschieben Sie Daten auf die GPU
In der Python-Version von PyTorch wird beim Lernen mit GPU cuDNN verwendet, um die Lerngeschwindigkeit zu verbessern.
Im Gegensatz zu C ++ bedeutet die Verbesserung der Lerngeschwindigkeit jedoch nicht, dass genau dieselbe Situation reproduziert werden kann, indem das Lernen erneut gedreht wird.
Daher besagt die PyTorch-Formel, dass zur Gewährleistung der Reproduzierbarkeit das Verhalten von cuDNN wie folgt deterministisch gemacht werden muss und gleichzeitig die Geschwindigkeit abnimmt.
https://pytorch.org/docs/stable/notes/randomness.html
Deterministic mode can have a performance impact, depending on your model. This means that due to the deterministic nature of the model, the processing speed (i.e. processed batch items per second) can be lower than when the model is non-deterministic.
Aus Sicht des Ingenieurs habe ich es in diesen Geschwindigkeitsvergleich aufgenommen, da ich möglicherweise besorgt bin über das Vorhandensein oder Nichtvorhandensein von Reproduzierbarkeit und die Geschwindigkeitsänderungen in Abhängigkeit vom Vorhandensein oder Fehlen von Reproduzierbarkeit.
Wenn Sie im Gegensatz zur "Rand" -Funktion von C ++ keinen Anfangswert der Zufallszahl festlegen, ist diese zufällig. Um die Reproduzierbarkeit in Python sicherzustellen, ** explizit ** den Anfangswert der Zufallszahl Sie müssen es einstellen. (Das Einstellen des Anfangswertes der Zufallszahl hat keinen Einfluss auf die Geschwindigkeit.)
Die Implementierung ist wie folgt.
deterministic.py
seed = 0
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
np.random.seed(seed)
random.seed(seed)
torch.backends.cudnn.deterministic = True #Definitiv statt langsamer zu werden
torch.backends.cudnn.benchmark = False #Definitiv statt langsamer zu werden
non_deterministic.py
torch.backends.cudnn.deterministic = False #Schneller statt nicht deterministisch
torch.backends.cudnn.benchmark = True #Beschleunigen Sie, wenn sich die Bildgröße nicht ändert
Selbst wenn der Inhalt, den Sie implementieren möchten, derselbe ist, können sich bei Änderung der Programmiersprache ** Notation und Regeln ** und ** erforderliche Bibliotheken ** ändern. Da sowohl Python als auch C ++ objektorientierte Sprachen sind, sind die Konzepte selbst ähnlich. Vor allem aber, da Python ein Interpretertyp und C ++ ein Kompilierungstyp ist, muss es implementiert werden, da die dynamische Typisierung für C ++ nicht funktioniert. Es sollte auch berücksichtigt werden, dass einige Funktionen nicht verfügbar sind, da die C ++ - API von PyTorch derzeit entwickelt wird.
Basierend auf diesen Punkten werde ich den Unterschied in der Implementierung zwischen Python und C ++ und dem von mir implementierten Programm vorstellen.
Neben dem derzeit allgemein geschriebenen Nutzungsstatus der Python-Bibliothek werden auch die für die Implementierung in C ++ empfohlene Bibliothek und der Nutzungsstatus der Bibliothek des Programms beschrieben, das ich tatsächlich geschrieben habe.
Python (empfohlen) | C++(Empfehlung) | C++(selbstgemacht) | |
---|---|---|---|
Umgang mit Befehlszeilenargumenten | argparse | boost::program_options | boost::program_options |
Modelldesign | torch.nn | torch::nn | torch::nn |
Vorverarbeitung (Transformation) | torchvision.transforms | torch::data::Transformationen (für verschiedene Vorverarbeitungen vor der Ausführung) or Selbst gemacht (bei verschiedenen Vorverarbeitungen nach der Ausführung) |
Selbst gemacht (mit OpenCV) |
Datensätze abrufen (Datensätze) | torchvision.Datensätze (mit Kissen) | Selbst gemacht (mit OpenCV) | Selbst gemacht (mit OpenCV) |
Dataloader | torch.utils.data.DataLoader | torch::data::make_data_Lader (zur Klassifizierung) or Selbst gemacht (außer Klassifizierung) |
Selbst gemacht (mit OpenMP) |
Verlustfunktion (Verlust) | torch.nn | torch::nn | torch::nn |
Optimierungsmethode (Optimierer) | torch.optim | torch::optim | torch::optim |
Fehler zurück Propagierungsmethode (rückwärts) | torch.Tensor.backward() | torch::Tensor::backward() | torch::Tensor::backward() |
Fortschrittsanzeige | tqdm | boost | selbstgemacht |
** Im Moment (24.03.2020) </ font> ** sieht es wie oben aus.
Bei Verwendung der PyTorch-Bibliothek in C ++ sind die Klassennamen und Funktionsnamen fast identisch mit Python. Dies scheint auf die Rücksichtnahme des Benutzers auf der Herstellerseite zurückzuführen zu sein. Ich bin sehr dankbar!
Als nächstes beschreibe ich die Punkte, auf die Sie beim Schreiben von PyTorch-Programmen in C ++ besonders achten sollten.
Das Folgende ist ein Auszug aus einem Teil des Programms, das ich geschrieben habe.
networks.hpp (Teilauszug)
using namespace torch;
namespace po = boost::program_options;
struct ConvolutionalAutoEncoderImpl : nn::Module{
private:
nn::Sequential encoder, decoder;
public:
ConvolutionalAutoEncoderImpl(po::Variables_map &vm);
torch::Tensor forward(torch::Tensor x);
}
TORCH_MODULE(ConvolutionalAutoEncoder);
Verwenden Sie beim Entwerfen eines Modells die Klasse "torch :: nn" wie in Python. Verwenden Sie beim Erstellen eines Modells auch eine Struktur. (Es gibt auch eine Klassenversion, aber es scheint ein wenig kompliziert) Zu diesem Zeitpunkt sollte beachtet werden, dass ** nn :: Module wie in Python </ font> ** geerbt wird. Dies entspricht dem Schreiben von Python.
Als nächstes ist es wichtig, die Struktur ** "[Modellname] Impl" </ font> ** und unter der Struktur ** Fügen Sie "TORCH_MODULE ([Modellname])" </ font> ** hinzu. Wenn Sie dies nicht tun, können Sie das Modell nicht speichern oder laden. Durch Setzen von "TORCH_MODULE ([Modellname])" kann die normale Struktur "ConvolutionalAutoEncoderImpl" als Struktur für das Modell "ConvolutionalAutoEncoder" deklariert werden, wahrscheinlich jedoch durch internes Erben der Klasse. Bist du da? (Erwartet) Daher wird beim Zugriff auf Elementvariablen wie "model-> to (device)" ** "->" (Pfeiloperator) </ font> ** Bitte beachten Sie, dass Sie verwenden müssen.
Als nächstes werden wir in Bezug auf die oben genannten Punkte die Punkte erläutern, die bei der Verwendung des nn-Klassenmoduls zu beachten sind. Sie können "nn :: Sequential" wie in Python verwenden. Verwenden Sie zum Hinzufügen eines Moduls zu "nn :: Sequential" in C ++ ** "push_back" </ font> ** wie den Vektortyp. Beachten Sie, dass Sie ** "->" (Pfeiloperator) </ font> ** verwenden sollten, um die Funktion "push_back" aufzurufen. Das Implementierungsbeispiel sieht wie folgt aus.
networks.cpp (Teilauszug / Modifikation)
nn::Sequential sq;
sq->push_back(nn::Conv2d(nn::Conv2dOptions(3, 64, /*kernel_size=*/4).stride(2).padding(1).bias(false)));
sq->push_back(nn::BatchNorm2d(64));
sq->push_back(nn::ReLU(nn::ReLUOptions().inplace(true)));
Wenn Sie selbst Transformationen, Datasets und Datenlader erstellen, wird ** ".clone ()" verwendet, um ** zu übergeben, wenn Tensortypdaten an andere Variablen übergeben werden. Ich war hier süchtig danach. Bezieht sich der Tensortyp auf die Handhabung von Berechnungsgraphen? (Erwartet) Wenn Sie es nicht so einstellen, kann sich der Wert im Tensor ändern.
transforms.cpp (Teilauszug)
void transforms::Normalize::forward(torch::Tensor &data_in, torch::Tensor &data_out){
torch::Tensor data_out_src = (data_in - this->mean) / this->std;
data_out = data_out_src.clone();
return;
}
Andere Programme sind fast die gleichen wie die Python-Version, und es macht nichts besonders süchtig. Außerdem habe ich meine eigene Klasse erstellt, die ich für etwas schwierig hielt, da sie sich von der Python-Version unterschied. Bitte beziehen Sie sich auf den folgenden GitHub für das spezifische Programm. https://github.com/koba-jon/pytorch_cpp/tree/master/ConvAE
Vielleicht schreibe ich einen Kommentar zum Quellcode. Wenn Sie der Meinung sind, dass "das seltsam ist", heißen wir Sie herzlich willkommen.
Grundsätzlich können Sie denken, dass es fast dasselbe ist, mit Ausnahme des Teils, dem nicht geholfen werden kann, z. B. dass die in Python vorhandene Bibliothek in C ++ nicht vorhanden ist. Sie können auch denken, dass Sie sich nicht vom GitHub-Programm geändert haben.
Insbesondere wurden die folgenden Inhalte beim Vergleich der Python-Version und der C ++ - Version vereinheitlicht.
Für jedes zu vergleichende Objekt wurden 182.340 64 × 64-Bilder von celebA verwendet, und ein Mini-Batch-Training eines durch 1 [Epoche] verschlungenen Auto-Encoder-Modells wurde durchgeführt, um den L1-Fehler zu minimieren. ** "Zeit pro [Epoche]" </ font> ** und ** "GPU-Speichernutzung" </ font> Ich überprüfte **.
Hier umfasst "Zeit pro [Epoche]" die Verarbeitungszeit von tqdm und selbst erstellten Funktionen. Ich habe dies aufgenommen, weil es fast keinen Einfluss auf die gesamte Verarbeitungszeit hatte und weil es bequemer ist, eine Visualisierung zu haben, wenn PyTorch tatsächlich verwendet wird, verwenden viele Leute es.
Zusätzlich wurden unter Verwendung des trainierten Modells 20.259 Testbilder einzeln in das Modell eingegeben und getestet. ** "Durchschnittliche Geschwindigkeit der Vorwärtsausbreitung" </ font> ** und ** "L1-Fehler zwischen Eingabebild und Ausgabebild" </ font> Ich habe auch> ** überprüft.
Dann habe ich gelernt und getestet, ohne etwas anderes als die "Ausführungsdatei" und "nvidia-smi" (die von Anfang an ausgeführt wurde, als Ubuntu gestartet wurde) zu starten.
CPU(Core i7-8700) | GPU(GeForce GTX 1070) | |||||
---|---|---|---|---|---|---|
Python | C++ | Python | C++ | |||
Nicht deterministisch th> | Definitiv th> | |||||
Lernen td> | Zeit [Zeit / Epoche] td> | 1 Stunde 04 Minuten 49 Sekunden td> | 1 Stunde 03:00 td> | 5 Minuten 53 Sekunden td> | 7 Minuten 42 Sekunden td> | 17 Minuten 36 Sekunden td> |
GPU-Speicher [MiB] td> | 2 | 9 | 933 | 913 | 2941 | |
Test td> | Geschwindigkeit [Sekunden / Daten] td> | 0.01189 | 0.01477 | 0.00102 | 0.00101 | 0.00101 |
L1-Fehler (MAE) td> | 0.12621 | 0.12958 | 0.12325 | 0.12104 | 0.13158 |
C ++ ist eine kompilierte Sprache. Daher dachte ich, dass ich die Interpretersprache Python schlagen würde ... aber ** beide waren gute Übereinstimmungen **.
In Bezug auf die Lernzeit haben wir festgestellt, dass die CPU fast gleich ist und die GPU mehr als doppelt so langsam wie C ++ als Python ist. (Warum?) Was dieses Ergebnis betrifft, ist die CPU ungefähr gleich, unterscheidet sich jedoch nur dann stark, wenn es sich um eine GPU handelt.
Wird wahrscheinlich erwähnt. Während die folgenden Leute experimentieren, scheint es keinen Zweifel zu geben, dass ** Python schneller ist </ font> **, wenn die GPU ausgeführt wird. https://www.noconote.work/entry/2019/01/11/151624
Außerdem sind die Geschwindigkeit und Leistung der Inferenz (Tests) fast identisch mit Python, sodass ** Python derzeit möglicherweise besser ist </ font> **.
Die Speichernutzung der GPU ist aus irgendeinem Grund ebenfalls groß. (Obwohl ReLUs Platz auf True gesetzt ist ...)
Es ist ein Ergebnis von Python (GPU) deterministisch und nicht deterministisch, aber wie die Formel klar sagt, ist deterministisch langsamer.
Immerhin wird sich die Zeit hier ändern.
Lerngeschwindigkeit
Argumentationsgeschwindigkeit
Leistung
Alle sind ungefähr gleich
Dieses Mal habe ich die Geschwindigkeit und Leistung von PyTorch für Python und C ++ verglichen.
Infolgedessen sind Python und C ++ in Bezug auf die Leistung fast gleich, so dass ich dachte, dass es kein Problem geben würde, PyTorch von ** C ++ ** zu verwenden. ** In diesem Stadium wird möglicherweise nicht empfohlen, C ++ PyTorch aus Gründen der Geschwindigkeit </ font> ** auszuführen.
Vielleicht befindet sich die C ++ - API noch in der Entwicklung und könnte in Zukunft erheblich verbessert werden! Von nun an ist dies meine Erwartung!
Recommended Posts