Dieser Artikel ist der Artikel zum 20. Tag von LeapMind Adventskalender 2019.
Ich werde darüber schreiben, wie die Zeit verkürzt werden kann, um das Ergebnis des Deep Learning-Modells zu erhalten, indem Socket und Signal für die Kommunikation zwischen Prozessen in C-Sprache verwendet und die Initialisierung im Voraus durchgeführt werden.
Ich bin normalerweise ein ** Python-basierter Deep Learning-Ingenieur **. Dieses Mal werde ich ** C Sprache ** verwenden. Es gibt viele Ingenieure, die C / C ++ in LeapMind verwenden, das eingebettete DL entwickelt, aber ich selbst entwickle selten mit C / C ++. ** Ich bin nicht sehr kompetent **. Wenn Sie also Fehler haben, lassen Sie es mich bitte wissen!
Der Titel lautet Prozessübergreifende Kommunikation für Embedded DL, aber es ist nur Prozessübergreifende Kommunikation! Eigentlich wollte ich mnist mit C / C ++ ausführen, aber da die Menge groß ist, ist der Inferenzteil Dummy (später beschrieben).
Übrigens gibt es bei eingebetteten Geräten wie der Fertigungsindustrie Zeiten, in denen Sie die Verarbeitung in kurzer Zeit durchführen möchten, z. B. ** einige zehn ms **. Wenn die Eingabe klein ist, kann sogar ein DL-Modell in zehn ms ausgeführt werden. Wenn Sie einfach das von TensorFlow erstellte DL-Modell laden, kann der Start ** einige Sekunden ** dauern.
In einem solchen Fall, indem Sie es als Dienst im Voraus starten und eine Anfrage senden, Sie können die Wartezeit vermeiden.
Tatsächlich verfügt Python, das häufig in DL verwendet wird, natürlich über ein Socket-Modul, und es gibt eine Bibliothek, die umfangreicher sein kann. Es gibt viele, aber in einer nahezu eingebetteten Umgebung möchten Sie möglicherweise Python installieren oder nicht. Daher müssen Sie möglicherweise in ** C / C ++ usw. ** entwickeln.
Die diesmal verwendete Umgebung ist wie folgt.
Ubuntu==16.04
gcc==5.4.0
Es sollte auf einer anderen Distribution oder einem anderen Mac funktionieren, und Sie können dasselbe unter Windows mit Winsock2 tun.
Es gibt mehrere Möglichkeiten, zwischen Prozessen zu kommunizieren. Dieses Mal verwenden wir die Socket-Kommunikation für die Kommunikation zwischen Prozessen. Als Bonus möchte ich auch eine einfache Methode mit Signal schreiben.
Die Kommunikationsverarbeitung von Client und Server erfolgt wie in der folgenden Abbildung dargestellt. Geben Sie als Inferenz eine 3x3-Matrix ein und berechnen Sie die Summe für jede Spalte. Eine 3x3-Matrix (9 Float-Spalten) wird vom Client gesendet, damit die 3 Float-Spalten kommunizieren.
Es gibt nicht viele Bücher über Sockets. Lesen Sie daher die technischen Blogs im Internet, um sich einen Überblick zu verschaffen, und ** für genaue Informationen ** [** Linux Programmer's Manual (Übersetzung) **](https: / Ich denke, es ist normal, /linuxjm.osdn.jp/html/LDP_man-pages/man7/socket.7.html) zu lesen und zu verstehen.
Referenzartikel / Materialien
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT 52579
#define SIZE 3
void inference(float* input, float* output)
{
for (size_t i=0; i<SIZE; ++i)
{
for (size_t j=0; j<SIZE; ++j)
{
output[i] += input[SIZE*i + j];
}
}
}
int main(void)
{
// create socket
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror("socket");
return 1;
}
// struct about the connection destination
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
// bind
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
perror("bind");
return 1;
}
// listen
int backlog = 1;
if (listen(sock, backlog) != 0)
{
perror("listen");
return 1;
} else
{
printf("listen\n");
}
while(1)
{
int status;
// accept socket
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connected_socks = accept(sock, (struct sockaddr *)&client, &len);
if (connected_socks == -1)
{
perror("accept");
} else
{
printf("accepted\n");
}
// recieve
float input[SIZE*SIZE] = {};
status = recv(connected_socks, input, sizeof(input), 0);
if (status == -1)
{
perror("recv");
} else
{
printf("received\n");
}
// print recieved data
printf("[ ");
for (size_t i=0; i<SIZE; ++i)
{
for (size_t j=0; j<SIZE; ++j)
{
printf("%f ", input[SIZE*i + j]);
}
}
printf("]\n");
// inference
float output[SIZE] = {};
inference(input, output);
// send
status = send(connected_socks, output, sizeof(output), 0);
if (status == -1)
{
perror("send");
} else
{
printf("send\n");
}
close(connected_socks);
}
close(sock);
return 0;
}
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 52579
#define SIZE 3
int main(void)
{
int status;
// create socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
// struct about the connection destination
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr("127.0.0.1");
// connect server
status = connect(sock, (struct sockaddr *)&server, sizeof(server));
if (status == -1)
{
perror("connect");
} else {
printf("connected\n");
}
// input
float input[SIZE*SIZE] = {
0.f, 1.f, 2.f,
3.f, 4.f, 5.f,
6.f, 7.f, 8.f
};
// send
status = send(sock, input, sizeof(input), 0);
if (status == -1)
{
perror("send");
} else {
printf("send\n");
}
// recieve
float output[SIZE] = {};
status = recv(sock, output, sizeof(output), 0);
if (status == -1)
{
perror("recv");
} else {
printf("received\n");
}
// print received data
printf("[ ");
for(size_t i=0; i<SIZE; ++i)
{
printf("%f ", output[i]);
}
printf("]\n");
// close socket
close(sock);
return 0;
}
gcc -o server server.c
gcc -o client client.c
$ ./server
listen
accepted
received
[ 0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000 8.000000 ]
send
./client
connected
send
received
[ 3.000000 12.000000 21.000000 ]
TensorFlow verfügt über eine C / C ++ - API. Es ist etwas eigenartig, sollte aber nach dem Lesen der Dokumentation relativ einfach zu bedienen sein (obwohl es etwas schwierig zu kompilieren ist ...). Dies allein einzuführen wird eine beträchtliche Menge sein. Es gibt viele Artikel und Referenzquellcodes. Bitte beziehen Sie sich auf diese.
Saitos Memo: Führen Sie ein in Python in C ++ erlerntes Netzwerk aus
In Tensorflow ist der Speicher bei der ersten Ausführung gesichert und die erste Operation ist langsam. Wenn Sie also nur zum ersten Mal leer ausführen, können Sie danach ohne Wartezeit ausführen.
Wenn Sie das Programm beenden möchten, können Sie das Programm mit "Strg + C" beenden, wobei die Betriebssystemfunktion "Signal" verwendet wird. Sie können das Programm auch mit Strg + Z stoppen.
Wenn das Programm gestartet und die Initialisierung wie das Laden des Modells abgeschlossen ist, wird es gestoppt, indem ein Stoppsignal an sich selbst gesendet wird. Durch Senden eines Signals von einem externen Programm oder Befehl kann das Programm ohne Wartezeit neu gestartet und ausgeführt werden. Es ist eine sehr primitive Methode, aber manchmal ist es gut, wenn es sehr einfach und gut für Demonstrationszwecke ist.
Es ist nicht möglich, Daten direkt über die Kommunikation zu übertragen, aber Sie können sie beispielsweise direkt von der Kamera lesen und ausführen oder die Datei einfach lesen.
run_by_signal.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
printf("Initialize...\n");
// run some initialization here
sleep(1);
pid_t c_pid = getpid();
printf("Send SIGCONT to PID: %d to run\n", c_pid);
while(1)
{
raise(SIGTSTP);
printf("run \n");
}
return 0;
}
send_signal.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char *argv[]) {
printf("send continue signal to PID:%s\n", argv[1]);
kill(atoi(argv[1]), SIGCONT);
}
gcc -o run_by_signal run_by_signal.c
gcc -o send_signal send_signal.c
Wenn run_by_signal
ausgeführt wird, wird es initialisiert und sofort gestoppt.
Danach wird durch Ausführen von "send_signal" "run_by_signal" neu gestartet, der Prozess ausgeführt (hier wird nur "run" ausgegeben) und sofort gestoppt.
Geben Sie bei der Ausführung von "send_signal" die Prozess-ID von "run_by_signal" an.
$ ./run_by_signal
Initialize...
Send SIGCONT to PID: 17088 to run
[1]+ Stopped ./run_by_signal
$ ./send_signal 17088
send continue signal to PID:17088
run
[1]+ Stopped ./run_by_signal
Sie können auch den Befehl kill
verwenden, um dasselbe wie send_signal
zu tun.
Lesen und verstehen Sie das Linux Programmer's Manual (Übersetzung) usw., um genaue Informationen zu diesem und dem endgültigen Signal zu erhalten. Ich finde das normal.
Ich dachte: "Ich benutze nur Python, deshalb möchte ich ab und zu die Sprache C verwenden." Ich schrieb darüber, wie man die Zeit verkürzt, um das Ergebnis des Deep Learning-Modells zu erhalten, indem man Socket und Signal für die Kommunikation zwischen Prozessen verwendet und die Initialisierung im Voraus durchführt.
Ich habe Tensorflow mit C ++ - API verwendet, aber ich habe keine C-API, daher würde ich es gerne ausprobieren. Außerdem scheint Pytorch eine C ++ - API zu haben, daher würde ich es gerne bald versuchen. Es scheint der Python-Oberfläche ähnlich zu sein, daher habe ich von jemandem gehört, der daran gewöhnt ist, dass es nützlich ist.