پرش به محتوا
← همهٔ یادداشت‌ها

TensorFlow روی GPU ابری: راه‌اندازی CUDA و بنچمارک سریع

·۷ دقیقه مطالعه·تیم جی‌پی‌یو

یک مکعب روبیک کلاسیک با خانه‌های سبز، نارنجی، قرمز و زرد در میانهٔ چرخش، روی سطح تیرهٔ مخملی و پس‌زمینهٔ چوبی.

اگر کد TensorFlow دارید و می‌خواهید آن را روی یک کارت گرافیک واقعی اجرا کنید، سریع‌ترین راه این است: یک ماشین GPU ابری را ساعتی اجاره کنید و قالب TensorFlow CUDA را بردارید. در این آموزش قدم‌به‌قدم مطمئن می‌شوید TensorFlow واقعاً کارت گرافیک را می‌بیند، یک بنچمارک ضرب ماتریس و یک بنچمارک آموزش مدل اجرا می‌کنید، و می‌فهمید کارتی که اجاره کرده‌اید برای کار شما بس است یا نه.

کل کار کمتر از یک ساعت طول می‌کشد. روی یک RTX 3060 با ۱۲ گیگابایت VRAM، هزینه‌اش در زمان نوشتن این مطلب از حدود ۲۱ هزار تومان در ساعت شروع می‌شود. لازم نیست چیزی روی لپ‌تاپ نصب کنید؛ فقط یک ترمینال و SSH.

پیش‌نیازها

یک حساب در جی‌پی‌یو لازم دارید؛ اگر ندارید از صفحهٔ ثبت‌نام شروع کنید. موجودی کیف پول باید حداقل به اندازهٔ یک ساعت از نرخ ماشینی باشد که انتخاب می‌کنید. برای این آموزش، شارژ حداقلی ۵۰ هزار تومان با کارت به کارت کافی است.

سومین پیش‌نیاز یک کلید SSH ثبت‌شده در بخش حساب کاربری است؛ ورود به ماشین فقط با کلید انجام می‌شود. اگر هنوز این‌ها را آماده نکرده‌اید، پست شروع اجاره GPU: اولین ماشین در ۱۰ دقیقه همه را قدم‌به‌قدم توضیح داده است.

انتخاب GPU ابری مناسب برای TensorFlow

بنچمارک‌های این آموزش کوچک‌اند و روی هر کارتی با ۸ گیگابایت VRAM یا بیشتر اجرا می‌شوند. اما هدف بنچمارک این است که کارت مناسب کار خودتان را پیدا کنید، پس بهتر است دست‌کم روی دو کارت امتحانش کنید:

  • اقتصادی: RTX 3060 با ۱۲ گیگابایت؛ در زمان نوشتن این مطلب از حدود ۲۱ هزار تومان در ساعت.
  • میانه: RTX A4000 با ۱۶ گیگابایت؛ در زمان نوشتن این مطلب از حدود ۳۷ هزار تومان در ساعت.
  • حافظهٔ زیاد: RTX 3090 با ۲۴ گیگابایت؛ در زمان نوشتن این مطلب از حدود ۴۷ هزار تومان در ساعت.
  • سریع: RTX 4090 با ۲۴ گیگابایت؛ در زمان نوشتن این مطلب از حدود ۱۳۱ هزار تومان در ساعت.

همهٔ این کارت‌ها و قیمت لحظه‌ای‌شان در بازار GPU هست و با فیلتر مدل کارت و حافظه پیدا می‌شوند. قیمت ساعتی در لحظهٔ اجاره قفل می‌شود. اگر نمی‌دانید کار شما چقدر حافظه می‌خواهد، پست انتخاب کارت گرافیک هوش مصنوعی: راهنمای VRAM هر کار را ببینید.

اجاره و راه‌اندازی با قالب TensorFlow CUDA

ماشین را انتخاب کنید و موقع اجاره، قالب TensorFlow CUDA را بردارید. این قالب بر پایهٔ ایمیج‌های رسمی TensorFlow است؛ یعنی درایور، CUDA و cuDNN از قبل با نسخهٔ TensorFlow جور شده‌اند و لازم نیست خودتان سراغشان بروید. دکمهٔ اجاره را بزنید و چند دقیقه صبر کنید تا وضعیت ماشین «در حال اجرا» شود.

صفحهٔ ماشین دستور SSH آماده را با یک کلیک کپی می‌دهد. شکل کلی‌اش این است:

ssh -p <PORT> root@<HOST>

اگر لینک Jupyter هم در صفحهٔ ماشین ظاهر شد، می‌توانید همهٔ کدهای این آموزش را در نوت‌بوک هم اجرا کنید؛ روش کار با آن در پست Jupyter روی GPU ابری: اولین کد PyTorch شما آمده است. این‌جا با SSH و اسکریپت جلو می‌رویم، چون بنچمارک تمیزتری می‌دهد.

مرحلهٔ ۱: آیا TensorFlow کارت گرافیک را می‌بیند؟

بعد از ورود، یک پوشهٔ کاری بسازید و اول ببینید سیستم کارت را می‌شناسد:

mkdir -p ~/tf-bench && cd ~/tf-bench
nvidia-smi

خروجی باید نام کارت، نسخهٔ درایور و مقدار VRAM را نشان بدهد. حالا همان بررسی‌ای را که مستندات رسمی TensorFlow پیشنهاد می‌کند اجرا کنید:

python3 -c "import tensorflow as tf; print(tf.__version__); print(tf.config.list_physical_devices('GPU'))"

اگر فهرستی مثل [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')] دیدید، همه‌چیز آماده است. اگر فهرست خالی بود، بخش «اگر به مشکل خوردید» را ببینید.

مرحلهٔ ۲: بنچمارک ضرب ماتریس

ضرب ماتریس‌های بزرگ قلب شبکه‌های عصبی است و سریع‌ترین راه برای سنجیدن توان خام کارت. این اسکریپت ضرب را با دقت float32 و float16 روی GPU و یک بار هم روی CPU اجرا می‌کند و زمان و توان تقریبی را برحسب TFLOPS چاپ می‌کند:

cat > bench_matmul.py <<'EOF'
import time
import tensorflow as tf

def bench(device, dtype, n=8192, iters=20):
    with tf.device(device):
        a = tf.random.normal((n, n), dtype=dtype)
        b = tf.random.normal((n, n), dtype=dtype)
        c = tf.matmul(a, b)  # warm-up
        _ = c.numpy()
        t0 = time.perf_counter()
        for _ in range(iters):
            c = tf.matmul(a, b)
        _ = c.numpy()
        dt = time.perf_counter() - t0
    tflops = 2 * n**3 * iters / dt / 1e12
    print(f"{device} {dtype.name}: {dt:.2f}s  ~{tflops:.1f} TFLOPS")

bench("/GPU:0", tf.float32)
bench("/GPU:0", tf.float16)
bench("/CPU:0", tf.float32, n=4096, iters=2)
EOF
python3 bench_matmul.py

دو نکته دربارهٔ این کد. اول، اجرای اولیه (warm-up) از زمان‌سنجی بیرون است، چون بار اول TensorFlow کتابخانه‌های CUDA را بار می‌کند. دوم، c.numpy() منتظر می‌ماند تا محاسبهٔ GPU واقعاً تمام شود؛ بدون آن، زمان‌سنجی غلط درمی‌آید چون عملیات GPU غیرهمگام اجرا می‌شوند.

عدد float16 روی کارت‌های سری RTX معمولاً به‌مراتب بالاتر از float32 است، چون از هسته‌های Tensor استفاده می‌کند. این‌جاست که مقایسهٔ دو کارت معنا پیدا می‌کند: عدد TFLOPS را کنار قیمت ساعتی هر کارت بگذارید تا ببینید هر تومان چقدر محاسبه می‌خرد.

مرحلهٔ ۳: بنچمارک آموزش یک مدل واقعی

ضرب ماتریس همهٔ داستان نیست؛ آموزش واقعی به خواندن داده و لایه‌های مختلف هم بستگی دارد. این اسکریپت یک شبکهٔ کانولوشنی کوچک را روی دیتاست CIFAR-10 پنج دور آموزش می‌دهد و زمان کل را چاپ می‌کند. دیتاست حدود ۱۷۰ مگابایت است و چون ماشین‌ها خارج از ایران‌اند، روی خود ماشین در چند ثانیه دانلود می‌شود:

cat > train_cifar.py <<'EOF'
import sys
import time
import tensorflow as tf
from tensorflow import keras

for gpu in tf.config.list_physical_devices("GPU"):
    tf.config.experimental.set_memory_growth(gpu, True)

if len(sys.argv) > 1 and sys.argv[1] == "mixed":
    keras.mixed_precision.set_global_policy("mixed_float16")

(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train = x_train.astype("float32") / 255.0
x_test = x_test.astype("float32") / 255.0

model = keras.Sequential([
    keras.Input(shape=(32, 32, 3)),
    keras.layers.Conv2D(64, 3, padding="same", activation="relu"),
    keras.layers.Conv2D(64, 3, padding="same", activation="relu"),
    keras.layers.MaxPooling2D(),
    keras.layers.Conv2D(128, 3, padding="same", activation="relu"),
    keras.layers.Conv2D(128, 3, padding="same", activation="relu"),
    keras.layers.MaxPooling2D(),
    keras.layers.Flatten(),
    keras.layers.Dense(256, activation="relu"),
    keras.layers.Dense(10),
    keras.layers.Activation("softmax", dtype="float32"),
])
model.compile(optimizer="adam",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])

t0 = time.perf_counter()
model.fit(x_train, y_train, batch_size=256, epochs=5,
          validation_data=(x_test, y_test))
print(f"train time: {time.perf_counter() - t0:.1f}s")
model.save("cifar_cnn.keras")
EOF
python3 train_cifar.py

در خروجی، Keras زمان هر دور (epoch) و دقت را نشان می‌دهد. دور اول همیشه کندتر است چون گراف ساخته و کامپایل می‌شود؛ برای مقایسهٔ کارت‌ها به دورهای دوم به بعد نگاه کنید. مقدار val_accuracy باید دور به دور بالا برود؛ اگر نمی‌رود، کد را دوباره بررسی کنید.

دقت ترکیبی (Mixed precision)

حالا همان اسکریپت را با دقت ترکیبی اجرا کنید:

python3 train_cifar.py mixed

در این حالت بیشتر محاسبات با float16 انجام می‌شود و وزن‌ها با float32 نگه داشته می‌شوند. لایهٔ آخر را عمداً float32 گذاشته‌ایم تا خروجی softmax پایدار بماند؛ این همان توصیهٔ راهنمای رسمی Mixed precision در TensorFlow است. روی کارت‌های RTX این حالت معمولاً زمان هر دور را کم می‌کند و VRAM کمتری هم می‌گیرد. زمان دو اجرا را مقایسه کنید.

خطی که حافظه را منطقی مصرف می‌کند

set_memory_growth باعث می‌شود TensorFlow به‌جای گرفتن تقریباً کل VRAM در همان لحظهٔ اول، فقط به اندازهٔ نیاز حافظه بگیرد. این‌طوری وقتی در ترمینال دیگری nvidia-smi می‌زنید، مصرف واقعی مدل را می‌بینید، نه کل حافظهٔ کارت را.

اگر به مشکل خوردید

فهرست GPU خالی است. اول nvidia-smi را بزنید. اگر کارت را نشان می‌دهد ولی TensorFlow نه، احتمالاً ماشین را با قالب دیگری اجاره کرده‌اید که TensorFlow نسخهٔ CPU دارد. ساده‌ترین راه، اجارهٔ دوباره با قالب TensorFlow CUDA است. راه دیگر، نصب نسخهٔ GPU طبق مستندات رسمی است:

python3 -m pip install 'tensorflow[and-cuda]'

خطای ResourceExhaustedError یا OOM. یعنی VRAM کم آمده. batch_size را نصف کنید یا دقت ترکیبی را روشن کنید. اگر کارتان ذاتاً حافظهٔ بیشتری می‌خواهد، ماشینی با ۲۴ گیگابایت بگیرید.

دور اول خیلی کند است. طبیعی است؛ بار کردن CUDA و کامپایل گراف وقت می‌برد. فقط اگر دورهای بعدی هم کند ماندند، با nvidia-smi در ترمینال دوم ببینید بار کارت (GPU-Util) بالا می‌رود یا نه.

دانلود CIFAR-10 نیمه‌کاره ماند. فایل خراب را پاک کنید و اسکریپت را دوباره اجرا کنید:

rm -rf ~/.keras/datasets/cifar-10*

هزینه و صرفه‌جویی

همهٔ این آموزش روی یک کارت کمتر از یک ساعت طول می‌کشد؛ روی RTX 3060، در زمان نوشتن این مطلب، کمتر از حدود ۲۱ هزار تومان. اگر می‌خواهید دو کارت را مقایسه کنید، اسکریپت‌ها را نگه دارید و روی کارت دوم فقط همان دو دستور python3 را بزنید؛ هر بنچمارک چند دقیقه بیشتر نیست.

هزینه دقیقه‌ای از کیف پول کم می‌شود و ماشین متوقف‌شده هزینه‌ای ندارد، پس بعد از کار ماشین را متوقف یا حذف کنید. دیسک ماشین با حذف آن پاک می‌شود؛ قبل از حذف، مدل و اسکریپت‌ها را روی لپ‌تاپ بیاورید:

scp -P <PORT> root@<HOST>:~/tf-bench/cifar_cnn.keras .

جزئیات انتقال فایل در پست دانلود مدل هوش مصنوعی روی سرور GPU: scp و rsync آمده است.

قدم بعدی

حالا که می‌دانید کارت چقدر توان دارد، اسکریپت آموزش خودتان را جای train_cifar.py بگذارید و همین مقایسه را با دادهٔ واقعی تکرار کنید. اگر بیشتر با PyTorch کار می‌کنید، قالب PyTorch و پست Jupyter همان مسیر را برای آن چارچوب نشان می‌دهند. برای جزئیات نصب و نسخه‌ها هم صفحهٔ نصب TensorFlow مرجع رسمی است.

پرسش‌های متداول

چطور بفهمم TensorFlow از GPU استفاده می‌کند؟

دستور tf.config.list_physical_devices('GPU') را اجرا کنید؛ اگر فهرست خالی نباشد، TensorFlow کارت را می‌بیند. حین آموزش هم در ترمینال دوم nvidia-smi بزنید؛ بالا رفتن GPU-Util و مصرف حافظه یعنی محاسبه واقعاً روی کارت گرافیک است.

برای TensorFlow چقدر VRAM لازم است؟

برای یادگیری و مدل‌های کوچک، ۸ تا ۱۲ گیگابایت کافی است. برای مدل‌های بزرگ‌تر یا تصویرهای با وضوح بالا، ۲۴ گیگابایت خیال را راحت می‌کند. دقت ترکیبی مصرف حافظه را کم می‌کند و گاهی همین، کارت ارزان‌تر را کافی می‌کند.

آیا TensorFlow روی ویندوز ابری هم اجرا می‌شود؟

ماشین‌های جی‌پی‌یو لینوکسی‌اند (کانتینر یا VM اوبونتو) و ویندوز ارائه نمی‌شود. خوشبختانه TensorFlow با GPU روی لینوکس بهترین پشتیبانی را دارد و قالب TensorFlow CUDA آماده تحویل داده می‌شود.

هزینهٔ اجرای این بنچمارک چقدر است؟

فقط به مدت روشن بودن ماشین بستگی دارد و دقیقه‌ای حساب می‌شود. در زمان نوشتن این مطلب، RTX 3060 از حدود ۲۱ هزار تومان در ساعت شروع می‌شود و کل این آموزش کمتر از یک ساعت طول می‌کشد.


تصویر کاور: «Rubik's cube, CN II» اثر kaʁstn (Carschten)، از ویکی‌مدیا کامنز، لایسنس CC BY-SA 3.0.