شبكتك العصبية الأولى على وحدة معالجة الرسومات (GPU). دليل المبتدئين


في هذه المقالة ، سأخبرك بكيفية إعداد بيئة تعلُم الآلة في 30 دقيقة ، وإنشاء شبكة عصبية للتعرف على الصور ، ثم تشغيل نفس الشبكة على معالج رسومات (GPU).

أولاً ، دعنا نحدد ما هي الشبكة العصبية.

في حالتنا ، هذا نموذج رياضي ، بالإضافة إلى تطبيقه للبرمجيات أو الأجهزة ، مبني على مبدأ تنظيم وعمل الشبكات العصبية البيولوجية - شبكات الخلايا العصبية للكائن الحي. نشأ هذا المفهوم في دراسة العمليات التي تحدث في الدماغ ، وفي محاولة لمحاكاة هذه العمليات.

الشبكات العصبية ليست مبرمجة بالمعنى المعتاد للكلمة ، فهي مدربة. القدرة على التعلم هي واحدة من المزايا الرئيسية للشبكات العصبية على الخوارزميات التقليدية. من الناحية الفنية ، يتكون التدريب من إيجاد معاملات الروابط بين الخلايا العصبية. في عملية التعلم ، تكون الشبكة العصبية قادرة على تحديد العلاقات المعقدة بين المدخلات والمخرجات ، وكذلك إجراء التعميم.

من وجهة نظر التعلم الآلي ، تعد الشبكة العصبية حالة خاصة لطرق التعرف على الأنماط ، والتحليل التمييزي ، وطرق التجميع ، وطرق أخرى.

معدات


أولاً ، دعنا نتعامل مع المعدات. نحن بحاجة إلى خادم مثبت عليه نظام التشغيل Linux. تتطلب معدات تشغيل أنظمة تعلُّم الآلة قوة كافية بما فيه الكفاية ، وبالتالي باهظة الثمن. بالنسبة لأولئك الذين ليس لديهم سيارة جيدة في متناول اليد ، أوصي بالاهتمام بعرض مقدمي الخدمات السحابية. يمكن استئجار الخادم الضروري بسرعة ودفع مقابل وقت الاستخدام فقط.

في المشاريع حيث يكون من الضروري إنشاء شبكات عصبية ، أستخدم خوادم أحد مزودي السحابة الروسية. تقدم الشركة خوادم سحابية مستأجرة خصيصًا للتعلم الآلي مع وحدات معالجة رسومات Tesla V100 القوية (GPUs) من NVIDIA. باختصار: يمكن أن يكون استخدام خادم مع وحدة معالجة رسومات أكثر كفاءة (بسرعة) عشرات المرات مقارنة بخادم مشابه في التكلفة حيث يتم استخدام وحدة المعالجة المركزية للحسابات (معالج مركزي معروف). يتم تحقيق ذلك بسبب تفاصيل هندسة GPU ، التي تتعامل مع الحسابات بشكل أسرع.

لتنفيذ الأمثلة الموضحة أدناه ، قمنا بشراء الخادم التالي لعدة أيام:

  • 150 جيجابايت SSD
  • رام 32 جيجا
  • معالج Tesla V100 16 Gb مع 4 نوى

تم تثبيت Ubuntu 18.04 على الجهاز.

اضبط البيئة


الآن تثبيت على الخادم كل ما تحتاجه للعمل. نظرًا لأن مقالنا مبدئيًا للمبتدئين ، سأتحدث فيه عن بعض النقاط التي ستكون مفيدة لهم.

يتم تنفيذ الكثير من العمل عند إعداد البيئة من خلال سطر الأوامر. يستخدم معظم المستخدمين Windows كنظام تشغيل. وحدة التحكم القياسية في نظام التشغيل هذا تترك الكثير مما هو مرغوب فيه. لذلك ، سوف نستخدم أداة Cmder / الأداة المناسبة . قم بتنزيل الإصدار المصغر وتشغيل Cmder.exe. بعد ذلك ، تحتاج إلى الاتصال بالخادم عبر SSH:

ssh root@server-ip-or-hostname

بدلاً من server-ip-or-hostname ، حدد عنوان IP أو اسم DNS لخادمك. بعد ذلك ، أدخل كلمة المرور وعند الاتصال الناجح ، يجب أن نحصل على شيء مثل هذا.

Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-74-generic x86_64)

اللغة الرئيسية لتطوير نماذج ML هي Python. والمنصة الأكثر شيوعًا لاستخدامه على Linux هي Anaconda .

تثبيته على الخادم الخاص بنا.

نبدأ بتحديث مدير الحزم المحلي:

sudo apt-get update

تثبيت curl (أداة سطر الأوامر):

sudo apt-get install curl

قم بتنزيل أحدث إصدار من Anaconda Distribution:

cd /tmp
curl –O https://repo.anaconda.com/archive/Anaconda3-2019.10-Linux-x86_64.sh

نبدأ التثبيت:

bash Anaconda3-2019.10-Linux-x86_64.sh

أثناء عملية التثبيت ، ستحتاج إلى تأكيد اتفاقية الترخيص. عند التثبيت بنجاح ، يجب أن ترى هذا:

Thank you for installing Anaconda3!

لتطوير نماذج ML ، يتم إنشاء العديد من الأطر الآن ، ونحن نعمل مع الأكثر شيوعًا: PyTorch و Tensorflow .

يسمح لك استخدام الإطار بزيادة سرعة التطوير واستخدام الأدوات الجاهزة للمهام القياسية.

في هذا المثال ، سنعمل مع PyTorch. تثبيته:

conda install pytorch torchvision cudatoolkit=10.1 -c pytorch

الآن نحن بحاجة إلى إطلاق Jupyter Notebook - أداة تطوير شعبية بين متخصصي ML. يسمح لك بكتابة الكود ورؤية نتائج تنفيذه على الفور. Jupyter Notebook هو جزء من Anaconda وهو مثبت بالفعل على خادمنا. تحتاج إلى الاتصال به من نظام سطح المكتب لدينا.

للقيام بذلك ، قمنا أولاً بتشغيل Jupyter على الخادم عن طريق تحديد المنفذ 8080:

jupyter notebook --no-browser --port=8080 --allow-root

بعد ذلك ، فتح علامة تبويب أخرى في وحدة تحكم Cmder (القائمة العلوية هي مربع حوار وحدة التحكم الجديدة) ، والاتصال على المنفذ 8080 بالخادم عبر SSH:

ssh -L 8080:localhost:8080 root@server-ip-or-hostname

عند إدخال الأمر الأول ، سيُعرض علينا روابط لفتح Jupyter في متصفحنا:

To access the notebook, open this file in a browser:
        file:///root/.local/share/jupyter/runtime/nbserver-18788-open.html
    Or copy and paste one of these URLs:
        http://localhost:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311
     or http://127.0.0.1:8080/?token=cca0bd0b30857821194b9018a5394a4ed2322236f116d311

استخدم الرابط للمضيف المحلي: 8080. انسخ المسار الكامل والصقه في شريط العنوان في المتصفح المحلي لجهاز الكمبيوتر الخاص بك. يفتح دفتر ملاحظات جوبيتر.

لنقم بإنشاء كمبيوتر محمول جديد: جديد - Notebook - Python 3.

تحقق من التشغيل الصحيح لجميع المكونات التي قمنا بتثبيتها. نقدم مثال كود PyTorch في Jupyter ونبدأ التنفيذ (زر التشغيل):

from __future__ import print_function
import torch
x = torch.rand(5, 3)
print(x)

يجب أن تكون النتيجة شيئًا مثل هذا:



إذا كان لديك نتيجة مماثلة ، فكلنا قمنا بإعدادها بشكل صحيح ويمكننا البدء في تطوير شبكة عصبية!

إنشاء شبكة عصبية


سننشئ شبكة عصبية للتعرف على الصور. نحن نأخذ هذا الدليل كأساس .

لتدريب الشبكة ، سنستخدم مجموعة بيانات CIFAR10 المتاحة للجمهور. لديه فئات: "طائرة" ، "سيارة" ، "طائر" ، "قطة" ، "أيل" ، "كلب" ، "ضفدع" ، "حصان" ، "سفينة" ، "شاحنة". يبلغ حجم الصور في CIFAR10 3 × 32 × 32 ، أي صور ملونة بثلاث قنوات بحجم 32 × 32 بكسل.


للعمل ، سنستخدم حزمة PyTorch التي تم إنشاؤها للعمل مع الصور - torchvision.

سنتخذ الخطوات التالية بالترتيب:

  • تنزيل وتطبيع مجموعات بيانات التدريب والاختبار
  • تعريف الشبكة العصبية
  • تدريب الشبكة على بيانات التدريب
  • اختبار الشبكة ببيانات الاختبار
  • كرر التدريب والاختبار GPU

سنقوم بتنفيذ جميع التعليمات البرمجية أدناه في Jupyter Notebook.

تنزيل وتطبيع CIFAR10


انسخ التعليمات البرمجية التالية ونفذها في Jupyter:


import torch
import torchvision
import torchvision.transforms as transforms

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

يجب أن يكون الجواب على هذا النحو:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Extracting ./data/cifar-10-python.tar.gz to ./data
Files already downloaded and verified

سنستخرج العديد من الصور التدريبية للتحقق:


import matplotlib.pyplot as plt
import numpy as np

# functions to show an image

def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))



تعريف الشبكة العصبية


دعونا نفحص أولاً كيف تعمل الشبكة العصبية للتعرف على الصور. هذه شبكة اتصال مباشرة بسيطة. يأخذ المدخلات ، ويمررها من خلال عدة طبقات واحدة تلو الأخرى ، ثم يعطي الناتج في النهاية.



لنقم بإنشاء شبكة مماثلة في بيئتنا:


import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

نحدد أيضًا وظيفة الخسارة والمحسن


import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

تدريب الشبكة على بيانات التدريب


نبدأ تدريب شبكتنا العصبية. أوجه انتباهك إلى حقيقة أنه بعد ذلك ، أثناء تشغيل هذا الرمز ، ستحتاج إلى الانتظار بعض الوقت حتى اكتمال العمل. استغرق مني 5 دقائق. يستغرق التواصل وقتًا.

 for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

نحصل على النتيجة التالية:



نحفظ نموذجنا المدرب:

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

اختبار الشبكة ببيانات الاختبار


قمنا بتدريب الشبكة باستخدام مجموعة من بيانات التدريب. لكننا بحاجة إلى التحقق مما إذا كانت الشبكة قد تعلمت أي شيء على الإطلاق.

سوف نتحقق من ذلك من خلال توقع تسمية الفئة التي تنتجها الشبكة العصبية ، والتحقق من الحقيقة. إذا كانت التوقعات صحيحة ، فإننا نضيف العينة إلى قائمة التوقعات الصحيحة.
دعنا نظهر الصورة من مجموعة الاختبار:

dataiter = iter(testloader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))



اطلب الآن من الشبكة العصبية أن تخبرنا بما يوجد في هذه الصور:


net = Net()
net.load_state_dict(torch.load(PATH))

outputs = net(images)

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))



تبدو النتائج جيدة جدًا: حددت الشبكة بشكل صحيح ثلاثًا من الصور الأربع.

دعونا نرى كيف تعمل الشبكة في مجموعة البيانات بأكملها.


correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))



يبدو أن الشبكة تعرف وتعمل. إذا حدد الفئات بشكل عشوائي ، فإن الدقة ستكون 10٪.

الآن دعنا نرى الفئات التي تحددها الشبكة بشكل أفضل:

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))



يبدو أن الشبكة تحدد السيارات والسفن بشكل أفضل: دقة 71٪.

لذلك تعمل الشبكة. الآن دعنا نحاول نقل عمله إلى معالج الرسوميات (GPU) ومعرفة التغييرات.

تدريب الشبكة العصبية GPU


أولاً ، سأشرح باختصار ما هو CUDA. CUDA (Compute Unified Device Architecture) هي منصة حوسبة متوازية طورتها NVIDIA للحوسبة العامة على وحدات معالجة الرسومات. مع CUDA ، يمكن للمطورين تسريع تطبيقات الحوسبة بشكل كبير باستخدام إمكانات وحدات معالجة الرسومات. تم تثبيت هذا النظام الأساسي بالفعل على خادمنا الذي اشتريناه.

دعنا أولاً نعرّف GPU كأول جهاز ظاهر.

device = torch . device ( "cuda:0" if torch . cuda . is_available () else "cpu" )
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print ( device )



أرسل الشبكة إلى GPU:

net.to(device)

سيتعين علينا أيضًا إرسال المدخلات والأهداف في كل خطوة وإلى وحدة معالجة الرسومات:

inputs, labels = data[0].to(device), data[1].to(device)

قم بتشغيل إعادة تدريب الشبكة بالفعل على GPU:

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
    inputs, labels = data[0].to(device), data[1].to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

هذه المرة ، استمر التدريب على الشبكة حوالي 3 دقائق. تذكر أن نفس المرحلة على المعالج العادي استمرت 5 دقائق. الفرق ليس كبيرا ، وذلك لأن شبكتنا ليست كبيرة. عند استخدام صفائف كبيرة للتدريب ، سيزداد الفرق بين سرعة GPU والمعالج التقليدي.

يبدو أن هذا كل شيء. ما تمكنا من القيام به:

  • لقد فحصنا ما هو GPU واخترنا الخادم الذي تم تثبيته عليه ؛
  • أنشأنا بيئة برمجية لإنشاء شبكة عصبية ؛
  • أنشأنا شبكة عصبية للتعرف على الصورة ودربناها ؛
  • كررنا تدريب الشبكة باستخدام GPU وحصلنا على زيادة في السرعة.

سأكون سعيدا للإجابة على الأسئلة في التعليقات.

All Articles