CUDA – chatgpz.de – KI-Tools & Praxiswissen https://chatgpz.de Unabhängige Tests, Anleitungen und Hintergründe zu KI-Tools. Tue, 16 Sep 2025 05:58:33 +0000 de-DE hourly 1 https://wordpress.org/?v=6.4.2 234900186 NVIDIA: CUDA von zusätzlichen Plattformen der dritten Occasion erhältlich https://chatgpz.de/data-science/nvidia-cuda-von-zusaetzlichen-plattformen-der-dritten-occasion-erhaeltlich/ https://chatgpz.de/data-science/nvidia-cuda-von-zusaetzlichen-plattformen-der-dritten-occasion-erhaeltlich/#respond Tue, 16 Sep 2025 05:58:33 +0000 https://chatgpz.de/data-science/nvidia-cuda-von-zusaetzlichen-plattformen-der-dritten-party-erhaeltlich/ [ad_1]

Diese Bemühungen erweitern vorhandene Wege, die Entwickler von CUDA -Zugriff haben, indem sie die Software program erhalten, die an einem Ort benötigt wird. Zusätzliche Händler kommen in Kürze.

Jede Verteilungsplattform, die CUDA umverteilt, umfasst:

  • Konsequentes Cuda-Toolkit-Namen: Drittanbieter-Pakete entsprechen den Nvidia-Namenskonventionen, um Verwirrung in Dokumentation und Tutorials zu vermeiden.

  • ZEIT COUDA-Updates: Pakete von Drittanbietern werden rechtzeitig aktualisiert, nachdem die offizielle NVIDIA-Beamte die Kompatibilität und die Verringerung der QS-Gemeinkosten senken können.

  • Weiterer freier Zugang: CUDA selbst bleibt frei verfügbar – selbst wenn sie in bezahlter Software program verpackt werden. Händler können den Zugriff auf ihre Pakete oder Software program berechnen, aber CUDA nicht speziell monetarisieren.

  • Umfassende Supportoptionen: Sie können über die Händler auf Help zugreifen und auch wie immer über NVIDIA -Foren oder die Entwicklerseite von NVIDIA helfen.

Das Erhalten von CUDA -Software program von NVIDIA ist kostenlos und die aktuellen Möglichkeiten, um CUDA zu erhalten (sie beinhalten das Herunterladen der Cuda Toolkitdas Ziehen CUDA -ContainerSet up für Python mit PIP oder Conda).

„Die Fähigkeit, CUDA in größeren Unternehmen und Software program -Anwendungen zu verbringen, ermöglicht es uns jedoch, Ihre Erfahrung als Entwickler einfach zu packen. Jonathan BentzAnwesend Rob Armstrong Und Anshuman Bhat. „Die Zusammenarbeit mit dem Nvidia -Ökosystem auf diese Weise ist ein bedeutender Meilenstein in unserer Mission, die Reibung bei der Bereitstellung von GPU -Software program zu verringern. Durch die Zusammenarbeit mit wichtigen Akteuren in der OS- und Paketmanagementlandschaft sorgt NVIDIA sicher, dass CUDA nach wie vor zugänglich, konsequent und benutzerfreundlich ist – keine Angelegenheit, an der sich Entwickler entscheiden.



[ad_2]

]]>
https://chatgpz.de/data-science/nvidia-cuda-von-zusaetzlichen-plattformen-der-dritten-occasion-erhaeltlich/feed/ 0 12423
Schreiben Sie Ihren ersten GPU -Kernel in Python mit Numba und Cuda https://chatgpz.de/data-analytics/schreiben-sie-ihren-ersten-gpu-kernel-in-python-mit-numba-und-cuda/ https://chatgpz.de/data-analytics/schreiben-sie-ihren-ersten-gpu-kernel-in-python-mit-numba-und-cuda/#respond Tue, 19 Aug 2025 06:40:03 +0000 http://chatgpz.de/data-analytics/schreiben-sie-ihren-ersten-gpu-kernel-in-python-mit-numba-und-cuda/ [ad_1]

Schreiben Sie Ihren ersten GPU -Kernel in Python mit Numba und CudaSchreiben Sie Ihren ersten GPU -Kernel in Python mit Numba und Cuda
Bild von Autor | Ideogramm

GPUs eignen sich hervorragend für Aufgaben, bei denen Sie den gleichen Betrieb über verschiedene Datenstücke hinweg ausführen müssen. Dies ist als die bekannt Einzelanweisung, mehrere Daten (SIMD) Ansatz. Im Gegensatz zu CPUs, die nur wenige leistungsstarke Kerne haben, haben GPUs Tausende kleinerer, die diese sich wiederholten Operationen auf einmal ausführen können. Sie sehen dieses Muster im maschinellen Lernen viel, beispielsweise beim Hinzufügen oder Multiplizieren großer Vektoren, da jede Berechnung unabhängig ist. Dies ist das ideale Szenario für die Verwendung von GPUs, um die Aufgaben parallelität zu beschleunigen.

Nvidia erstellt CUDA als eine Möglichkeit für Entwickler, Programme zu schreiben, die auf der GPU anstelle der CPU ausgeführt werden. Es basiert auf C und ermöglicht es Ihnen, spezielle Funktionen mit dem Namen Kernel zu schreiben, die viele Operationen gleichzeitig ausführen können. Das Downside ist, dass das Schreiben von CUDA in C oder C ++ nicht gerade anfängerfreundlich ist. Sie müssen sich mit Dingen wie Schaltspeicherzuweisung, Thread -Koordination und dem Verständnis der Funktionsweise der GPU auf einer niedrigen Ebene befassen. Dies kann überwältigend sein, insbesondere wenn Sie es gewohnt sind, Code in Python zu schreiben.

Hier Numba kann Ihnen helfen. Es ermöglicht das Schreiben von Cuda-Kerneln mit Python mit der Compiler-Infrastruktur von LLVM (Digital Machine), um Ihren Python-Code direkt zu CUDA-kompatiblen Kerneln zu kompilieren. Mit Simply-in-Time (JIT) -Kompilation können Sie Ihre Funktionen mit einem Dekorateur kommentieren und Numba alles andere für Sie behandelt.

In diesem Artikel werden wir ein gemeinsames Beispiel für die Addition des Vektors verwenden und einfachen CPU -Code in einen Cuda -Kernel mit Numba konvertieren. Die Addition der Vektor ist ein ideales Beispiel für die Parallelität, da die Zugabe über einen einzelnen Index unabhängig von anderen Indizes ist. Dies ist das perfekte SIMD -Szenario, sodass alle Indizes gleichzeitig hinzugefügt werden können, um die Vektor -Addition in einem Vorgang zu vervollständigen.

Beachten Sie, dass Sie eine CUDA -GPU benötigen, um diesem Artikel zu folgen. Sie können verwenden Colabs Kostenlose T4 -GPU oder eine lokale GPU mit NVIDIA -Toolkit und NVCC installiert.

# Einrichten der Umgebung und Set up von Numba

Numba ist als Python -Paket erhältlich und Sie können es mit PIP installieren. Darüber hinaus werden wir verwenden Numpy Für Vektoroperationen. Richten Sie die Python -Umgebung mit den folgenden Befehlen ein:

python3 -m venv venv
supply venv/bin/activate
pip set up numba-cuda numpy

# Vektoraddition auf der CPU

Nehmen wir ein einfaches Beispiel für die Addition von Vektor. Für zwei gegebene Vektoren fügen wir die entsprechenden Werte aus jedem Index hinzu, um den Endwert zu erhalten. Wir werden Numpy verwenden, um zufällige Float32 -Vektoren zu erzeugen und die endgültige Ausgabe mit A for Loop zu generieren.

import numpy as np 

N = 10_000_000 # 10 million components 
a = np.random.rand(N).astype(np.float32) 
b = np.random.rand(N).astype(np.float32) 
c = np.zeros_like(a) # Output array 

def vector_add_cpu(a, b, c): 
    """Add two vectors on CPU""" 
    for i in vary(len(a)): 
        c(i) = a(i) + b(i)

Hier ist eine Aufschlüsselung des Codes:

  • Initialisieren Sie jeweils zwei Vektoren mit 10 Millionen zufälligen Gleitkomma-Zahlen
  • Wir erstellen auch einen leeren Vektor c das Ergebnis speichern
  • Der vector_add_cpu Funktion schaltet einfach jeden Index durch und fügt die Elemente aus a Und bdas Ergebnis speichern c

Dies ist a Serienbetrieb; Jede Zugabe erfolgt nacheinander. Dies funktioniert zwar einwandfrei, ist jedoch nicht der effizienteste Ansatz, insbesondere für große Datensätze. Da jede Ergänzung unabhängig von den anderen ist, ist dies ein perfekter Kandidat für die parallele Ausführung bei einer GPU.

Im nächsten Abschnitt sehen Sie, wie Sie dieselbe Operation für die GPU mit Numba konvertieren. Durch die Verteilung der einzelnen Elemente über Tausende von GPU-Threads können wir die Aufgabe erheblich schneller erledigen.

# Vektor Addition auf der GPU mit Numba

Sie werden nun Numba verwenden, um eine Python -Funktion zu definieren, die auf CUDA ausgeführt werden kann, und sie innerhalb von Python ausführen kann. Wir führen den gleichen Vektor -Addition -Betrieb durch, aber jetzt kann es für jeden Index des Numpy -Arrays parallel ausgeführt werden, was zu einer schnelleren Ausführung führt.

Hier ist der Code zum Schreiben des Kernels:

from numba import config

# Required for newer CUDA variations to allow linking instruments. 
# Prevents CUDA toolkit and NVCC model mismatches.
config.CUDA_ENABLE_PYNVJITLINK = 1

from numba import cuda, float32

@cuda.jit
def vector_add_gpu(a, b, c):
	"""Add two vectors utilizing CUDA kernel"""
	# Thread ID within the present block
	tx = cuda.threadIdx.x
	# Block ID within the grid
	bx = cuda.blockIdx.x
	# Block width (variety of threads per block)
	bw = cuda.blockDim.x

	# Calculate the distinctive thread place
	place = tx + bx * bw

	# Be certain that we do not exit of bounds
	if place < len(a):
    	    c(place) = a(place) + b(place)

def gpu_add(a, b, c):
	# Outline the grid and block dimensions
	threads_per_block = 256
	blocks_per_grid = (N + threads_per_block - 1) // threads_per_block

	# Copy knowledge to the machine
	d_a = cuda.to_device(a)
	d_b = cuda.to_device(b)
	d_c = cuda.to_device(c)

	# Launch the kernel
	vector_add_gpu(blocks_per_grid, threads_per_block)(d_a, d_b, d_c)

	# Copy the consequence again to the host
	d_c.copy_to_host(c)

def time_gpu():
	c_gpu = np.zeros_like(a)
	gpu_add(a, b, c_gpu)
	return c_gpu

Lassen Sie uns aufschlüsseln, was oben passiert.

// Verständnis der GPU -Funktion

Der @cuda.jit Der Dekorateur fordert Numba auf, die folgende Funktion als Cuda -Kernel zu behandeln. Eine spezielle Funktion, die parallel über viele Threads der GPU ausgeführt wird. Zur Laufzeit kompiliert Numba diese Funktion mit CUDA-kompatiblen Code und verarbeitet die C-API-Transpilation für Sie.

@cuda.jit
def vector_add_gpu(a, b, c):
	...

Diese Funktion wird gleichzeitig auf Tausenden von Threads ausgeführt. Wir brauchen jedoch eine Möglichkeit, herauszufinden, an welchem Teil der Daten jeder Thread funktionieren sollte. Das tun die nächsten Zeilen:

  • tx ist die ID des Threads in seinem Block
  • bx ist die ID des Blocks im Netz
  • bw ist wie viele Fäden in einem Block sind

Wir kombinieren diese mit Berechnen Sie eine einzigartige Placewas jedem Thread mitteilt, welches Component der Arrays es hinzufügen sollte. Beachten Sie, dass die Threads und Blöcke möglicherweise nicht immer einen gültigen Index liefern, da sie in Befugnissen von 2. wirken. Dies kann zu ungültigen Indizes führen, wenn die Vektorlänge nicht der zugrunde liegenden Architektur entspricht. Daher fügen wir eine Schutzbedingung hinzu, um den Index zu validieren, bevor wir die Vektor -Addition durchführen. Dies verhindert, dass beim Zugriff auf das Array einen außergewöhnlichen Laufzeitfehler ist.

Sobald wir die einzigartige Place kennen, können wir die Werte jetzt genau wie für die CPU -Implementierung hinzufügen. Die folgende Zeile entspricht der CPU -Implementierung:

c(place) = a(place) + b(place)

// Begin des Kernels

Der gpu_add Funktion stellt Dinge auf:

  • Es definiert, wie viele Themen und Blöcke verwendet werden sollen. Sie können mit unterschiedlichen Werten der Block- und Fadengrößen experimentieren und die entsprechenden Werte im GPU -Kernel drucken. Dies kann Ihnen helfen, zu verstehen, wie die zugrunde liegende GPU -Indexierung funktioniert.
  • Es kopiert die Eingangsarrays (aAnwesend bUnd c) vom CPU -Speicher zum GPU -Speicher, sodass die Vektoren im GPU -RAM zugänglich sind.
  • Es führt den GPU -Kernel mit vector_add_gpu(blocks_per_grid, threads_per_block).
  • Schließlich kopiert es das Ergebnis von der GPU zurück in die c Array, damit wir auf die Werte der CPU zugreifen können.

# Vergleich der Implementierungen und der potenziellen Beschleunigung

Jetzt, da wir sowohl die CPU- als auch die GPU -Versionen von Vektorabzug haben, ist es Zeit zu sehen, wie sie sich vergleichen. Es ist wichtig, die Ergebnisse und den Ausführungsschub zu überprüfen, den wir mit CUDA -Parallelität erhalten können.

import timeit

c_cpu = time_cpu()
c_gpu = time_gpu()

print("Outcomes match:", np.allclose(c_cpu, c_gpu))

cpu_time = timeit.timeit("time_cpu()", globals=globals(), quantity=3) / 3
print(f"CPU implementation: {cpu_time:.6f} seconds")

gpu_time = timeit.timeit("time_gpu()", globals=globals(), quantity=3) / 3
print(f"GPU implementation: {gpu_time:.6f} seconds")

speedup = cpu_time / gpu_time
print(f"GPU speedup: {speedup:.2f}x")

Zunächst führen wir beide Implementierungen aus und überprüfen, ob ihre Ergebnisse übereinstimmen. Dies ist wichtig, um sicherzustellen, dass unser GPU -Code korrekt funktioniert und die Ausgabe mit den CPUs übereinstimmt.

Als nächstes verwenden wir Pythons integriert timeit Modul, um zu messen, wie lange jede Model dauert. Wir führen jede Funktion ein paar Mal aus und nehmen den Durchschnitt, um ein zuverlässiges Timing zu erhalten. Schließlich berechnen wir, wie oft schneller die GPU -Model mit der CPU verglichen wird. Sie sollten einen großen Unterschied sehen, da die GPU viele Operationen gleichzeitig ausführen kann, während die CPU sie jeweils in einer Schleife umgeht.

Hier ist die erwartete Ausgabe der T4 -GPU von NVIDIA auf Colab. Beachten Sie, dass sich die genaue Beschleunigung basierend auf CUDA -Versionen und der zugrunde liegenden {Hardware} unterscheiden kann.

Outcomes match: True
CPU implementation: 4.033822 seconds
GPU implementation: 0.047736 seconds
GPU speedup: 84.50x

Dieser einfache Take a look at hilft, die Leistung der GPU -Beschleunigung zu demonstrieren und warum er für Aufgaben, die große Datenmengen und parallele Arbeiten beinhalten, so nützlich ist.

# Einpacken

Und das ist es. Sie haben jetzt Ihren ersten Cuda -Kernel mit Numba geschrieben, ohne tatsächlich einen C- oder CUDA -Code zu schreiben. Numba ermöglicht eine einfache Schnittstelle zur Verwendung der GPU durch Python und macht es für Python -Ingenieure viel einfacher, mit CUDA -Programmierung zu beginnen.

Sie können jetzt dieselbe Vorlage verwenden, um erweiterte CUDA -Algorithmen zu schreiben, die im maschinellen Lernen und im tiefen Lernen häufig vorkommen. Wenn Sie nach dem SIMD -Paradigma ein Downside finden, ist es immer eine gute Idee, GPU zur Verbesserung der Ausführung zu verwenden.

Der komplette Code ist in Colab Pocket book verfügbar, auf das Sie zugreifen können Hier. Fühlen Sie sich frei, es zu testen und einfache Änderungen vorzunehmen, um ein besseres Verständnis dafür zu erhalten, wie CUDA -Indexierung und Ausführung intern funktioniert.

Kanwal Mehreen ist ein Ingenieur für maschinelles Lernen und technischer Schriftsteller mit einer tiefgreifenden Leidenschaft für die Datenwissenschaft und die Schnittstelle von KI mit Medizin. Sie hat das eBook „Produktivität mit Chatgpt maximieren“. Als Google -Era -Gelehrte 2022 für APAC setzt sie sich für Vielfalt und akademische Exzellenz ein. Sie wird auch als Teradata -Vielfalt in Tech Scholar, MITACS Globalink Analysis Scholar und Harvard Wecode Scholar anerkannt. Kanwal ist ein leidenschaftlicher Verfechter der Veränderung, nachdem er Femcodes gegründet hat, um Frauen in STEM -Bereichen zu stärken.

[ad_2]

]]>
https://chatgpz.de/data-analytics/schreiben-sie-ihren-ersten-gpu-kernel-in-python-mit-numba-und-cuda/feed/ 0 11704
Pipelination AI/ML Coaching Workloads mit CUDA -Streams https://chatgpz.de/machine-learning/pipelination-ai-ml-coaching-workloads-mit-cuda-streams/ https://chatgpz.de/machine-learning/pipelination-ai-ml-coaching-workloads-mit-cuda-streams/#respond Fri, 27 Jun 2025 08:56:07 +0000 http://chatgpz.de/machine-learning/pipelination-ai-ml-training-workloads-mit-cuda-streams/ [ad_1]

Neunter in unserer Serie auf Leistungsprofilierung und Optimierung in Pytorch Ziel, die kritische Rolle der Leistungsanalyse und -optimierung bei der Entwicklung des maschinellen Lernens hervorzuheben. Während der gesamten Serie haben wir eine Vielzahl praktischer Instruments und Techniken zur Analyse und Steigerung der Laufzeitleistung von Pytorch-basierten KI/ML-Modellen überprüft. Unser Ziel battle zweifach:

  1. Betonung der Bedeutung der routinemäßigen Bewertung und Optimierung von AI/ML -Workloads.
  2. Demonstration der Zugänglichkeit von Instruments und Techniken mit großer Sorten zur Analyse und Optimierung der Laufzeitleistung der KI/ML. Sie müssen kein CUDA -Experte sein, um Ihre Modellleistung sinnvoll zu verbessern und die Rechenkosten zu senken.

In diesem Beitrag werden wir die Verwendung von CUDA -Streams untersuchen, ein leistungsstarkes Merkmal des CUDA -Programmiermodells von NVIDIA, das eine ausgefeilte Methode zur Überlappung von GPU -Operationen und gleichzeitig ausführen kann. Obwohl wir normalerweise unsere Arbeitsbelastung für KI/ML -Modelltraining mit einem einzigen monolithischen (auch bekannt als „unzerbrechlichen“) Berechnungsdiagramm assoziieren G Wenn Sie auf der GPU ausgeführt werden, gibt es einige Szenarien, in denen die Grafik in zwei unterschiedliche Untergraphen zerlegt werden kann G1 Und G2Wo G = g2*g1. In solchen Fällen ermöglichen CUDA -Streams das „Pipelining“ des Berechnungsdiagramms, dh unser Trainingsschritt, um zu laufen G1 (Batch -Eingabe N+1) parallel zu G2 (auf dem nth Ausgabe von G1). Diese Technik ist besonders nützlich, wenn:

  • Keiner der Subgraph nutzt die GPU, wenn sie alleine ausgeführt werden, und
  • Die beiden Untergraphen haben ähnliche Rechenkosten (dh weder dominiert die Laufzeit).

Wir werden zwei gemeinsame Szenarien untersuchen, in denen „Pipelining“ machbar ist:

  1. Teilmodell-Coaching oder -Fecker:
    Es ist üblich, ein vorgebildetes Modell einzufrieren Rückgrat (z. B. Function Extractor oder Encoder) und trainieren nur ein Modell Kopf (zB, Decoder). Seit der Gefrorenen Rückgrat verlässt sich nicht auf Gradienten aus dem Kopfdie beiden können gleichzeitig ausgeführt werden.
  2. Abladen von Datenvorverarbeitung in die GPU:
    Eine gemeinsame Methode zur Behandlung von Engpässen in der Eingabepipeline (auch als GPU -Starvation bezeichnet) kann die Datenvorverarbeitung in die GPU verschoben werden. Während die Vorbereitung der Vorverarbeitungsvorgänge in der Modelldiagramme die Leistung verbessert, können zusätzliche Gewinne erzielt werden, indem die Vorverarbeitung auf einem separaten CUDA -Stream parallel zur Modellausführung ausgeführt wird. Die Verarbeitung der Vorverarbeitung ist im Vergleich zu Modellkomputenten nicht trivial.

Um unsere Diskussion zu erleichtern, werden wir zwei Spielzeugtrainingsskripte definieren und die Trainingsleistung unter verschiedenen Szenarien messen. Die Experimente wurden auf einem durchgeführt Amazon EC2 G5.2XLARGE Instanz (mit einer NVIDIA A10G -GPU und 8 VCPUs) ausgeführt a Pytorch (2.6) Deep Studying AMI (Dlami).

Bitte beachten Sie: Die Code -Snippets, die wir teilen, dienen nur zu Demonstrationszwecken – bitte verlassen sich nicht auf ihre Richtigkeit oder Optimalität. Die Auswirkungen der Verwendung von CUDA -Streams variieren je nach Modellarchitektur und Systemkonfiguration. Wir ermutigen Sie, Ihr eigenes Profiling und Experimentieren durchzuführen, bevor Sie CUDA -Streams (oder eine andere Device -Technik, auf die wir uns beziehen) in Ihren Workflow integrieren.

Teil 1: Pipelination eines Encoder-Decoder-Modells

Der erste Anwendungsfall, den wir untersuchen, beinhaltet ein CNN-basierter Bildsegmentierungsmodell, das aus einem festen (vorgeborenen) Encoder und einem trainierbaren Decoder besteht. In diesem Szenario kann der Encoder unabhängig vom Coaching des Decoders unabhängig von der Schulung des Decoders ausgeführt werden, da die Encodergewichte eingefroren und nicht betroffen sind. In diesem Abschnitt bewerten wir die Auswirkungen der Pipelination des Trainingsprozesses mithilfe von CUDA -Streams.

Ein Spielzeugbild -Trainingsexperiment

Wir beginnen mit der Definition eines einfachen CNN-basierten Bildcodierers zusammen mit seinem entsprechenden Decoder.

undefined

Als nächstes erstellen wir einen synthetischen Datensatz mit zufälligen Bildern und Segmentierungskarten.

from torch.utils.knowledge import DataLoader
from torchvision.datasets.imaginative and prescient import VisionDataset

# A dataset with random pictures and per-pixel labels
class FakeDataset(VisionDataset):
    def __init__(self):
        tremendous().__init__(root=None)
        self.dimension = 1000000

    def __getitem__(self, index):
        # create a random picture
        img = torch.randint(0, 256, (3, img_size, img_size),
                            dtype=torch.uint8)

        # create a random label map
        goal = torch.randint(0, num_classes, (img_size, img_size))

        return img, goal

    def __len__(self):
        return self.dimension

train_set = FakeDataset()

train_loader = DataLoader(
    dataset=train_set,
    batch_size=8,
    num_workers=8
)

Schließlich definieren wir die Verlustfunktion, die Optimierer und die Trainingsschleife. Beachten Sie, dass wir die Gewichte des Encoders einfrieren und nur den Decoder trainieren.

import time

gadget = torch.gadget("cuda")
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(decoder.parameters())

# Freeze the encoder weights
encoder.requires_grad_(False)
encoder.eval().to(gadget)

decoder.prepare().to(gadget)

warmup = 10
active_batches = 100
total_iters = warmup + active_batches

for idx, knowledge in enumerate(train_loader):
    inputs = knowledge(0).to(gadget=gadget, non_blocking=True).float()
    labels = knowledge(1).to(gadget=gadget, non_blocking=True)
    optimizer.zero_grad()
    with torch.no_grad():
        options = encoder(inputs)
    output = decoder(options)
    loss = criterion(output, labels)
    loss.backward()
    optimizer.step()

    if idx == warmup:
        # sync the GPU and begin the timer
        torch.cuda.synchronize()
        t0 = time.perf_counter()

    if idx == total_iters:
        break

# look ahead to the GPU to finnish after which cease the timer
torch.cuda.synchronize()
total_time = time.perf_counter() - t0
print(f'throughput: {active_batches / total_time}')

Unser Foundation -Trainingskript erreicht einen durchschnittlichen Durchsatz von 83 Schritten professional Sekunde mit einer durchschnittlichen GPU -Auslastung von 85%.

Pipelination der Modellausführung mit CUDA -Strömen

In der überarbeiteten Model der unten gezeigten Trainingsschleife stellen wir zwei CUDA -Streams vor: einen für die Ausführung des Encoders und eine für die Schulung des Decoders. In jeder Iteration führen wir zwei Operationen gleichzeitig aus:

  1. Trainieren Sie den Decoder mit den Bildfunktionen und Etiketten von Batch N.
  2. Führen Sie den Encoder auf der Eingabestapel aus N+1 Um seine Bildmerkmale zu erzeugen.
encoder_stream = torch.cuda.Stream()
decoder_stream = torch.cuda.Stream()

# initialize the options to None
options = None

for idx, knowledge in enumerate(train_loader):
    inputs = knowledge(0).to(gadget, non_blocking=True).float()
    labels_next = knowledge(1).to(gadget, non_blocking=True)

    if options just isn't None:
        with torch.cuda.stream(decoder_stream):
            decoder_stream.wait_stream(encoder_stream)

            optimizer.zero_grad()
            output = decoder(options)
            loss = criterion(output, labels)
            loss.backward()
            optimizer.step()

    with torch.cuda.stream(encoder_stream):
        with torch.no_grad():
            options =  encoder(inputs)
        # Report that options was produced on s1_backbone
        options.record_stream(encoder_stream)

    labels = labels_next

    if idx == warmup:
        # sync the GPU and begin the timer
        torch.cuda.synchronize()
        t0 = time.perf_counter()
    if idx == total_iters:
        break

# look ahead to the GPU to complete after which cease the timer
torch.cuda.synchronize()
total_time = time.perf_counter() - t0
print(f'throughput: {active_batches / total_time}')

Diese Modifikation ergibt einen durchschnittlichen Durchsatz von 91 Schritten professional Sekunde, was eine Beschleunigung von 9,6% entspricht. Dies ist eine signifikante Verbesserung – insbesondere angesichts der Tatsache, dass unsere Grundlinie bereits eine hohe GPU -Nutzung hatte (85%).

Empfindlichkeit der Pipelination gegenüber Workload -Eigenschaften

Die Effektivität der Pipelinierung mit CUDA -Streams hängt stark von den Besonderheiten der Trainingsloadung und der Laufzeitumgebung ab. Wenn der Encoder deutlich größer ist als der Decoder (oder umgekehrt), kann Pipelining wenig Nutzen bieten oder sogar die Leistung behindern. Umgekehrt neigt die Pipelination, wenn die GPU nicht ausreichend genutzt wird, zu erheblicheren Gewinnen.

Um diese Abhängigkeit zu veranschaulichen, können wir das Experiment mit unterschiedlichen Chargengrößen umgeben. Die Ergebnisse sind nachstehend zusammengefasst:

Auswirkungen der Pipelination mit CUDA -Strömen auf den Durchsatz (vom Autor)

Wenn die Chargengröße zunimmt, nimmt der Nutzen der Pipelinierung ab. Dies liegt wahrscheinlich daran, dass größere Stapelgrößen natürlich zu einer höheren (und effizienteren) GPU -Nutzung führen und durch gleichzeitige Ausführung weniger Raum für Verbesserungen hinterlassen.

Teil 2: Vergrößerungen auf die GPU abladen

In diesem Abschnitt werden wir die Verwendung von CUDA -Streams auf die Beschleunigung der Datenvergrößerung anwenden. In früheren Weblog -Posts (z. B.,, Hier Und Hier) Wir haben das Drawback der Engpässe auf der Dateneingangspipeline aus verschiedenen Perspektiven untersucht und verschiedene Techniken zur Diagnose und Ansprache überprüft. Eine häufige Ursache für diese Engpässe ist die Erschöpfung der CPU -Ressourcen, bei der die CPU die Rechenanforderungen der Vorverarbeitungspipeline nicht erfüllen kann. Das Ergebnis ist die GPU -Starvation – ein Szenario, in dem die teure GPU im Leerlauf sitzt und auf die Ankunft von Daten wartet.

Eine effektive Lösung besteht darin, starke Datenvorverarbeitung in die GPU zu laden. Wir werden diese Technik demonstrieren und einen Schritt weiter gehen, indem wir die Augmentationen auf einem dedizierten CUDA -Stream ausführen, wodurch die gleichzeitige Ausführung mit dem Modelltraining ermöglicht wird.

Ein Spielzeugbild -Klassifizierungs -Trainingsexperiment

Wir beginnen mit der Definition eines einfachen CNN-basierten Bildklassifizierungsmodells:

import torch
import torch.nn as nn

import torch
import torch.nn as nn

img_size = 256
num_classes = 10
mannequin = nn.Sequential(
    # Begin with 256x256 picture
    nn.Conv2d(3, 16, kernel_size=1),
    nn.ReLU(inplace=True),
    nn.Conv2d(16, 32, kernel_size=2, stride=2),  # 2x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(32, 64, kernel_size=2, stride=2),  # 4x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(64, 128, kernel_size=2, stride=2),  # 8x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(128, 256, kernel_size=2, stride=2),  # 16x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(256, 512, kernel_size=2, stride=2),  # 32x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(512, 1024, kernel_size=2, stride=2),  # 64x downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(1024, 2048, kernel_size=2, stride=2),  # 128X downsample
    nn.ReLU(inplace=True),
    nn.Conv2d(2048, 4096, kernel_size=2, stride=2),  # 256X
    nn.Flatten(),
    nn.Linear(4096, num_classes)
)

Als nächstes erstellen wir einen synthetischen Datensatz mit einer Augmentation -Pipeline, die absichtlich zu einem schwerwiegenden Leistungs Engpass ausgelegt ist:

import random
from torch.utils.knowledge import DataLoader
import torchvision.transforms.v2 as T
from torchvision.datasets.imaginative and prescient import VisionDataset
import torchvision.transforms.v2.useful as F
import torchvision.ops as ops

# A dataset with random pictures and labels
class FakeDataset(VisionDataset):
    def __init__(self, remodel = None):
        tremendous().__init__(root=None, remodel=remodel)
        self.dimension = 1000000

    def __getitem__(self, index):
        # create a random picture
        img = torch.randint(0, 256, (3, img_size, img_size),
                           dtype=torch.uint8)
        # create a random label
        goal = torch.randint(0, num_classes, (1, ))

        if self.remodel:
            # Apply tranformations
            img = self.remodel(img)

        return img, goal

    def __len__(self):
        return self.dimension

augmentations = T.Compose((
    T.ToDtype(torch.float32),
    T.RandomCrop(img_size//2),
    T.Resize(img_size),
    T.RandomRotation(levels=45.0),
    T.GaussianBlur(kernel_size=7),
    T.Normalize(imply=(0, 0, 0), std=(1, 1, 1))
))

train_set = FakeDataset(remodel=augmentations)

train_loader = DataLoader(
    dataset=train_set,
    batch_size=32,
    num_workers=8
)

Schließlich definieren wir die Verlustfunktion, die Optimierer und die Trainingsschleife:

import time

gadget = torch.gadget("cuda")
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(mannequin.parameters())

mannequin.prepare().to(gadget)

warmup = 10
active_batches = 100
total_iters = warmup + active_batches

for idx, knowledge in enumerate(train_loader):
    inputs = knowledge(0).to(gadget=gadget, non_blocking=True)
    labels = knowledge(1).to(gadget=gadget, non_blocking=True).squeeze()
    optimizer.zero_grad()
    output = mannequin(inputs)
    loss = criterion(output, labels)
    loss.backward()
    optimizer.step()

    if idx == warmup:
        # sync the GPU and begin the timer
        torch.cuda.synchronize()
        t0 = time.perf_counter()

    if idx == total_iters:
        break

# look ahead to the GPU to finnish after which cease the timer
torch.cuda.synchronize()
total_time = time.perf_counter() - t0
print(f'throughput: {active_batches / total_time}')

Das Ausführen dieses Basiskripts führt zu einem durchschnittlichen Durchsatz von 20,41 Schritten professional Sekunde und einer GPU -Auslastung von nur 42%. Die schweren Datenvergrößerungen ersticken die CPU, die zum GPU -Starvation führt. Sehen Sie unsere Vorheriger Beitrag Weitere Informationen zum Erkennen von Engpässen auf der Dateneingangspipeline.

Ableiten von Datenvergrößerungen in die GPU

Um den Leistungs Engpass für die Dateneingangspipeline zu beheben, verschieben wir die Augmentationen auf die GPU.

Der erste Schritt ist zu definieren Benutzerdefinierte Datenumstellungen Dadurch werden zufällige Drehungen und Pflanzen professional Probe in einer Stapel angewendet. Dies ist wichtig, weil der eingebaute Einbau Torchvision Transformationen wenden die gleiche Augmentation über die gesamte Cost an-und verliert die Zufälligkeit der Probenprobe auf der CPU.

Wir implementieren die Batchrandomcrop Verwenden Sie mit dem roi_align Operator.

class BatchRandomCrop(T.Rework):
    def __init__(self, output_size):
        tremendous().__init__()
        self.output_size = output_size

    def remodel(self, img: torch.Tensor, params: dict):
        batch_size, _, original_height, original_width = img.form
        gadget = img.gadget
        max_top = original_height - self.output_size
        max_left = original_width - self.output_size

        # Generate random prime and left coords for every picture within the batch
        random_top = torch.randint(0, max_top + 1, (batch_size,),
                                   gadget=gadget, dtype=torch.float32)
        random_left = torch.randint(0, max_left + 1, (batch_size,),
                                    gadget=gadget, dtype=torch.float32)

        image_indices = torch.arange(batch_size, gadget=gadget,
                                     dtype=torch.float32)

        packing containers = torch.stack((
            image_indices,
            random_left,
            random_top,
            random_left + self.output_size,
            random_top + self.output_size
        ), dim=1)

        cropped_batch = ops.roi_align(
            img,
            packing containers,
            output_size=self.output_size
        )
        return cropped_batch 

Wir implementieren die Batchrandomrotate Überschreiten Sie durch Iterien über alle Bilder in der Stapel und wenden Sie eine zufällige Drehung auf jeden an. Beachten Sie, dass diese Model nicht vektorisiert ist; Eine vollständig vektorisierte Implementierung wäre mehr mehr Aufwand.

class BatchRandomRotation(T.Rework):
    def __init__(self, levels):
        tremendous().__init__()
        self .levels = levels

    def remodel(self, inpt: torch.Tensor, params: dict):
        # break up the batch into an inventory of particular person pictures
        pictures = record(torch.unbind(inpt, dim=0))

        augmented_images = ()
        for img_tensor in pictures:
            # generate a random angle
            angle = random.uniform(-self.levels, self.levels)

            # apply the rotation to the one picture
            transformed_img = F.rotate(
                img_tensor,
                angle=angle
            )
            augmented_images.append(transformed_img)

        # stack the reworked pictures
        return torch.stack(augmented_images, dim=0)

Wir definieren jetzt batch_transform Dies ahmt die oben definierte CPU-basierte Augmentation-Pipeline nach:

batch_transform = T.Compose((
    T.ToDtype(torch.float32),
    BatchRandomCrop(img_size//2),
    T.Resize(img_size),
    BatchRandomRotation(levels=45.0),
    T.GaussianBlur(kernel_size=7),
    T.Normalize(imply=(0, 0, 0), std=(1, 1, 1))
)) 

Schließlich setzen wir den Datensatz zurück und aktualisieren die Trainingsschleife, um das neue anzuwenden batch_transform:

train_set = FakeDataset(remodel=None)

train_loader = DataLoader(
    dataset=train_set,
    batch_size=32,
    num_workers=8
)

for idx, knowledge in enumerate(train_loader):
    inputs = knowledge(0).to(gadget=gadget, non_blocking=True)
    labels = knowledge(1).to(gadget=gadget, non_blocking=True).squeeze()
    
    # apply augmentations
    inputs = batch_transform(inputs)
    
    optimizer.zero_grad()
    output = mannequin(inputs)
    loss = criterion(output, labels)
    loss.backward()
    optimizer.step()

    if idx == warmup:
        torch.cuda.synchronize()
        t0 = time.perf_counter()

    if idx == total_iters:
        break

torch.cuda.synchronize()
total_time = time.perf_counter() - t0
print(f'throughput: {active_batches / total_time}')

Dieses aktualisierte Trainingsskript verbessert den Durchsatz auf 35,22 Schritte professional Sekunde – eine Beschleunigung von 72,57% gegenüber dem Basisergebnis.

Pipelining -Augmentationen mit CUDA -Strömen

Als nächstes pipeline wir die Augmentations- und Trainingsschritte mit zwei separaten CUDA -Streams: Eine zum Ausführen der Datenumwandlung für das Coaching des Modells. In jeder Iteration der Schleife führen wir zwei gleichzeitige Operationen aus:

  1. Wir trainieren das Modell auf der Augmented Cost N.
  2. Führen Sie GPU-basierte Datenvergrößerungen auf Batch durch N+1
transform_stream = torch.cuda.Stream()
model_stream = torch.cuda.Stream()

# initialize the reworked worth to None
reworked = None

for idx, knowledge in enumerate(train_loader):
    inputs = knowledge(0)
    labels_next = knowledge(1)

    if reworked just isn't None:
        with torch.cuda.stream(model_stream):
            labels = labels.to(gadget, non_blocking=True).squeeze()
            model_stream.wait_stream(transform_stream)
            optimizer.zero_grad()
            output = mannequin(reworked)
            loss = criterion(output, labels)
            loss.backward()
            optimizer.step()

    with torch.cuda.stream(transform_stream):
        inputs = inputs.to(gadget, non_blocking=True)
        reworked = batch_transform(inputs)
        # Report that the tensor was produced on transform_stream
        reworked.record_stream(transform_stream)

    labels = labels_next

    if idx == warmup:
        torch.cuda.synchronize()
        t0 = time.perf_counter()
    if idx == total_iters:
        break

torch.cuda.synchronize()
total_time = time.perf_counter() - t0
print(f'throughput: {active_batches / total_time}')

Dies verbessert den Durchsatz weiter auf 38,82 Schritte professional Sekunde – ein Anstieg der serialisierten Lösung um 10,2% und 90,20% schneller als die ursprüngliche Basislinie

Empfindlichkeit der Pipelination gegenüber Workload -Eigenschaften

Wie wir in Teil 1 gesehen haben, variiert der Vorteil der Pipelination unter Verwendung von CUDA -Streams je nach Particulars der Arbeitsbelastung. In der folgenden Tabelle erfassen wir die Ergebnisse für verschiedene Chargengrößen:

Auswirkungen der Pipelination mit CUDA -Strömen auf den Durchsatz (vom Autor)

Mit zunehmender Stapelgröße wird die GPU -Abladung effektiver und steigert die Leistung erheblich. Gleichzeitig nehmen die Gewinne durch Pipelining ab. Dies gilt wahrscheinlich für die Tatsache, dass größere Chargengrößen die GPU -Effizienz erhöhen und die Überlappungsmöglichkeiten verringern.

Zusammenfassung

Wenn es darum geht, KI/ML -Workloads auszuführen, zählt jede Millisekunden. In diesem Beitrag haben wir die Auswirkungen der Pipelination eines KI/ML -Trainingsschritts unter Verwendung von CUDA -Stream in zwei gemeinsamen Szenarien untersucht: Teilmodelltraining und Ausladung von Datenvergrößerungen in die GPU. In beiden Fällen übertraf die pipelierte Lösung die serialisierte Implementierung – obwohl das Ausmaß der Verbesserung aufgrund des Werts der Chargengröße signifikant variierte.

Wie wir während des gesamten Beitrags betont haben, kann die erwartete Auswirkungen der Verwendung von CUDA -Streams je nach KI/ML -Arbeitsbelastung stark variieren. In Fällen, in denen die GPU bereits effizient genutzt wird, kann der Overhead bei der Verwendung von CUDA -Streams tatsächlich zu einer Verschlechterung der Laufzeitleistung führen. Wir empfehlen dringend, diese Technik auf Ihren eigenen Workloads zu testen, bevor wir diesen Ansatz verfolgen.

Wir hoffen, dass Sie die in diesem Beitrag beschriebene Technik nützlich finden. Weitere Tipps, Methods und Techniken zum Profilieren und Optimieren von KI/ML -Workflows finden Sie in den anderen Posts in dieser Serie.

[ad_2]

]]>
https://chatgpz.de/machine-learning/pipelination-ai-ml-coaching-workloads-mit-cuda-streams/feed/ 0 10314
Steigern Sie Ihren Python-Code mit CUDA. Richten Sie Ihre GPU ganz einfach mit Numbas… | aus von Thomas Reid | November 2024 https://chatgpz.de/machine-learning/steigern-sie-ihren-python-code-mit-cuda-richten-sie-ihre-gpu-ganz-einfach-mit-numbas-aus-von-thomas-reid-november-2024/ https://chatgpz.de/machine-learning/steigern-sie-ihren-python-code-mit-cuda-richten-sie-ihre-gpu-ganz-einfach-mit-numbas-aus-von-thomas-reid-november-2024/#respond Thu, 21 Nov 2024 03:07:43 +0000 http://chatgpz.de/machine-learning/steigern-sie-ihren-python-code-mit-cuda-richten-sie-ihre-gpu-ganz-einfach-mit-numbas-aus-von-thomas-reid-november-2024/ [ad_1]

Bild von AI (Dalle-3)

Richten Sie Ihre GPU ganz einfach mit Numbas CUDA JIT aus

Ich habe bereits über die Python-Bibliothek Numba geschrieben. Schauen Sie sich meinen Artikel über den untenstehenden Hyperlink an.

Der TL;DR des oben Gesagten conflict, dass ich gezeigt habe, wie Sie mit Numba eine deutliche Beschleunigung Ihres Python-Codes erzielen können. Numba ist eine leistungsstarke Python-Bibliothek, die Ihren Code auf Geschwindigkeit optimiert. Im Kern ist Numba ein Simply-In-Time-Compiler (JIT), der eine Teilmenge des Python- und NumPy-Codes in schnellen Maschinencode übersetzt. Dieser Prozess erfolgt automatisch und dynamisch und ermöglicht es Python-Entwicklern, mit minimalen Änderungen an ihrem ursprünglichen Python-Code echte Leistungsverbesserungen zu erzielen.

Beim regulären Numba-JIT-Compiler geht es darum, die Codelaufzeit für Ihre CPU zu optimieren. Wenn Sie jedoch das Glück haben, Zugriff auf eine GPU zu haben, zeige ich Ihnen in diesem Artikel, wie Sie Numba erneut verwenden können, dieses Mal mit dessen CUDA JIT, um Ihren Python-Code noch weiter zu beschleunigen, indem es auf die GPU abzielt, auf der der Code ausgeführt wird.

Voraussetzungen

[ad_2]

]]>
https://chatgpz.de/machine-learning/steigern-sie-ihren-python-code-mit-cuda-richten-sie-ihre-gpu-ganz-einfach-mit-numbas-aus-von-thomas-reid-november-2024/feed/ 0 5340