Wenn Sie den Code für Chainer-Funktionen lesen, werden möglicherweise Aufrufe von "cuda.elementwise" oder "cuda.reduce" angezeigt. Dies sind Methoden zum Ausführen Ihrer eigenen Verarbeitung auf der GPU. Man kann sagen, dass es eine unverzichtbare Methode zur Implementierung der Chainer-Funktion ist und es notwendig erscheint, ein Zwischen-Chainer zu werden, also habe ich es untersucht. Dieser Artikel befasst sich mit "cuda.elementwise" und behandelt "cuda.reduce" nicht.
Eine Beschreibung von cuda.elementwise
finden Sie unten.
http://docs.chainer.org/en/stable/cupy-reference/kernel.html
Darüber hinaus hat Slide Share einen Kommentar von Herrn Okuda von Preferred Networks.
http://www.slideshare.net/ryokuta/cupy
cuda.elementwise
definiert den CUDA-Kernel.
Der CUDA-Kernel ist ein Programm, das auf einer GPU ausgeführt wird, die CUDA unterstützt.
Durch Aufrufen von "cuda.elementwise" erhält der Rückgabewert eine Funktion zum Aufrufen des CUDA-Kernels, und durch Aufrufen der Funktion zum Aufrufen des Kernels wird der CUDA-Kernel auf der GPU ausgeführt.
Die Substanz der Kernel-Aufruffunktion ist das ElementwiseKernel-Objekt, das aufgerufen werden kann.
Als erstes Beispiel werden wir alle Elemente eines bestimmten Arrays inkrementieren. Importieren Sie zunächst die erforderlichen Module wie unten gezeigt und definieren Sie "xp" als Referenz auf "cuda.cupy". Der nachfolgende Beispielcode setzt voraus, dass Sie den folgenden Code ausführen:
import numpy as np
import chainer
from chainer import cuda
xp = cuda.cupy
Verwenden Sie dann cuda.elementwise
, um die Array-Elemente zu erhöhen.
x = xp.asarray([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
y = cuda.elementwise(
'T x',
'T y',
'y = x + 1;',
'sample1_fwd',
)(x)
print(y)
Die Ausgabe sieht folgendermaßen aus: Sie können sehen, dass alle Elemente inkrementiert sind. Die Ausgabe nach der Ausgabe von vor dauert einige Zeit, aber es scheint, dass die Kompilierung von nvcc hinter den Kulissen erfolgt.
[[ 2. 3. 4.]
[ 5. 6. 7.]]
"cuda.elementwise" wird in zwei Stufen wie folgt verwendet. Im eigentlichen Code werden jedoch häufig zwei Prozesse zusammen beschrieben, z. B. "cuda.elementwise (...) (x)".
cuda.elementwise
auf, um eine Kernel-Aufruffunktion zu generierenDie Erklärung des Argument- / Rückgabewerts von cuda.elementwise
ist nicht diese Methode, sondern [Document of cupy.ElementwiseKernel
](http://docs.chainer.org/en/stable/cupy-reference/kernel.html# Es ist in cupy.ElementwiseKernel) beschrieben.
cuda.elementwise
ruft intern cupy.ElementwiseKernel
auf und beide Argumente sind gleich (außer dass name
in cuda.elementwise
erforderlich ist).
Die folgenden Argumente sind für "cuda.elementwise" erforderlich. Es gibt andere optionale Argumente, aber ich habe sie noch nicht vollständig verstanden, daher werde ich sie nicht erklären.
in_params
Gibt die Zeichenfolge an, die die Eingabeargumente deklariert. Das Argument erfordert einen Typ und einen Argumentnamen. Wenn die Typzeichenfolge aus einem Zeichen besteht, handelt es sich um einen Platzhalter vom Typ **. Der durch den Typ-Platzhalter dargestellte Typ ist der Typ der Variablen, die beim Ausführen der Kernel-Aufruffunktion übergeben wird. Dies ist nützlich, wenn Sie Variablen desselben Typs deklarieren möchten.
Im Beispielcode war in_params wie folgt.
'T x',
Das Folgende wird durch diese Zeichenfolge ausgedrückt.
out_params
Gibt die Zeichenfolge an, die die Ausgabeargumente deklariert. Wie in_params erfordert das Argument einen Typ und einen Argumentnamen.
Im Beispielcode lautete out_params wie folgt.
'T y',
Das Folgende wird durch diese Zeichenfolge ausgedrückt.
operation
Geben Sie die Zeichenfolge an, die den auszuführenden Prozess definiert. Im Beispielcode wurde "y" durch "x + 1" ersetzt.
'y = x + 1;',
name
Der Name des Prozesses. Bei der Implementierung des Moduls unter chainer.functions ist es "Funktionsname_fwd" für die Vorwärtsverarbeitung und "Funktionsname_bwd" für die Rückwärtsverarbeitung.
Durch Ausführen der Kernel-Aufruffunktion wird der definierte CUDA-Kernel ausgeführt. Übergeben Sie die Variable in_params zum Zeitpunkt der Ausführung als Argument. Die Variable, die out_params entspricht, kann weggelassen werden, sie kann jedoch auch explizit übergeben werden, indem sie nach der Variablen angegeben wird, die in_params entspricht. Der Rückgabewert ist das in out_params angegebene Argument. Wenn für out_params mehrere Argumente angegeben werden, sind diese Tupel der Rückgabewert.
Übergeben wir auch einen Wert an out_params. Übergeben Sie einfach den Wert für out_params an das Argument der Kernel-Aufruffunktion.
x = xp.asarray([[1, 2, 3], [4, 5, 6]], dtype=np.float32)
y = xp.asarray([[1, 1, 1], [2, 2, 2]], dtype=np.float32)
y = cuda.elementwise(
'T x',
'T y',
'y += x;',
'sample2_fwd',
)(x, y)
print(y)
Das Ausführungsergebnis ist wie folgt und x wird zum ursprünglichen y hinzugefügt.
[[ 2. 3. 4.]
[ 6. 7. 8.]]
Broadcasting
Das Array wird automatisch gesendet.
x = xp.asarray([1, 2, 3], dtype=np.float32)
y = xp.asarray([[1, 1, 1], [2, 2, 2]], dtype=np.float32)
y = cuda.elementwise(
'T x',
'T y',
'y += x;',
'sample3_fwd',
)(x, y)
print(y)
Ausführungsergebnis:
[[ 2. 3. 4.]
[ 3. 4. 5.]]
Wenn die Größe des Arrays nicht übereinstimmt und Sie nicht senden können, tritt ein Fehler auf.
x = xp.asarray([1, 2], dtype=np.float32)
y = xp.asarray([[1, 1, 1], [2, 2, 2]], dtype=np.float32)
y = cuda.elementwise(
'T x',
'T y',
'y += x;',
'sample4_fwd',
)(x, y)
print(y)
Ausführungsergebnis:
Traceback (most recent call last):
File "elementwise_sample.py", line 61, in <module>
)(x, y)
File "cupy\core\elementwise.pxi", line 508, in cupy.core.core.ElementwiseKernel.__call__ (cupy\core\core.cpp:34118)
File "cupy\core\elementwise.pxi", line 334, in cupy.core.core._broadcast (cupy\core\core.cpp:31734)
File "cupy\core\core.pyx", line 1504, in cupy.core.core.broadcast.__init__ (cupy\core\core.cpp:50697)
ValueError: Broadcasting failed
Indexing
Sie möchten häufig einen Index angeben, wenn Sie mit einem Array arbeiten. Sie können den Index wie folgt angeben.
_ind.size ()
repräsentiert die Anzahl der IndizesHier ist ein Beispiel, das die Elemente eines Arrays umkehrt.
x = xp.asarray([1, 2, 3, 4], dtype=np.float32)
y = xp.zeros_like(x, dtype=np.float32)
y = cuda.elementwise(
'raw T x',
'T y',
'y = x[_ind.size() - i - 1];',
'sample5_fwd',
)(x, y)
print(y)
Das Ausführungsergebnis ist wie folgt.
[ 4. 3. 2. 1.]
Sie können sich den obigen Code so vorstellen, dass der folgende Code mit Numpy auf einer GPU ausgeführt wird.
x = np.asarray([1, 2, 3, 4], dtype=np.float32)
y = np.zeros_like(x, dtype=np.float32)
i = np.arange(4)
y = x[4 - i - 1]
Beachten Sie, dass Sie "y" an die Kernel-Aufruffunktion übergeben müssen. Wenn Sie "y" nicht wie unten gezeigt übergeben, wird der Fehler "Wert Fehler: Schleifengröße ist unentschlossen" angezeigt. Dies scheint zu passieren, weil Sie den Index nicht nur mit rohen Argumenten dimensionieren können.
x = xp.asarray([1, 2, 3, 4], dtype=np.float32)
y = xp.zeros_like(x, dtype=np.float32)
y = cuda.elementwise(
'raw T x',
'T y',
'y = x[_ind.size() - i - 1];',
'sample6_fwd',
)(x)
print(y)
Betrachten Sie das Erhalten von "x [t [i]]" (i = 0, 1, 2, ...), wenn "x" ein zweidimensionales Array und "t" ein eindimensionales Array ist. .. Dies kann wie folgt geschrieben werden.
x = xp.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32)
t = xp.asarray([0, 2, 1], dtype=np.int32)
y = cuda.elementwise(
'raw T x, S t',
'T y',
'int ind[] = {i, t}; y = x[ind];',
'sample7_fwd',
)(x, t)
print(y)
Ausführungsergebnis:
[ 1. 6. 8.]
"int ind [] = {i, t};" erzeugt einen Index, der [(0, t [0]), (1, t [1]), (2, t [2])] anzeigt. ..
Sie können die C-Syntax (nvcc? Um genau zu sein) wie for
und while
verwenden.
Berechnen Sie als Beispiel den kumulativen Wert für jede Spalte von x.
Stellen Sie sicher, dass "y [i, j]" von "x [0, j]" bis "x [i, j]" kumulativ ist.
x = xp.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.float32)
y = xp.zeros_like(x)
y = cuda.elementwise(
'raw T x, int32 c',
'raw T y',
'''
int ind[] = {0, i};
y[ind] = x[ind];
for (int j = 1; j < c; j++) {
int ind[] = {j, i};
int prev_ind[] = {j - 1, i};
y[ind] = y[prev_ind] + x[ind];
}
''',
'sample8_fwd',
)(x, x.shape[0], y, size=x.shape[1])
print(y)
Ausführungsergebnis:
[[ 1. 2. 3.]
[ 5. 7. 9.]
[ 12. 15. 18.]]
Sie können auch CUDA-Funktionen verwenden. Es ist jedoch nicht bekannt, wie viel Unterstützung es hat. Verwenden wir als Beispiel "atomicAdd".
x = xp.zeros((3, 3), dtype=np.float32)
t = xp.asarray([0, 1, 2], dtype=np.int32)
y = cuda.elementwise(
'S t',
'raw T x',
'int ind[] = {i, t}; atomicAdd(&x[ind], 1);',
'sample9_fwd',
)(t, x)
print(y)
Ausführungsergebnis:
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
Wenn Sie "cuda.elementwise" verstehen, werden Sie Ihr Verständnis von Chainer vertiefen.
cuda.elementwise
und cuda.reduce
werden häufig in Chainer verwendet. Wenn Sie mehr wissen möchten, sollten Sie sich auf sie beziehen.
Recommended Posts