تدريب وضبط مَعلمات PyTorch على ضبط نموذج PyTorch على Cloud AI Platform

1. نظرة عامة

في هذا التمرين العملي، ستتعرّف على سير عمل كامل لتدريب نماذج تعلُّم الآلة على Google Cloud باستخدام PyTorch لإنشاء نموذجك. من بيئة Cloud AI Platform Notebooks، ستتعرّف على كيفية تجميع مهمة التدريب لتشغيلها على AI Platform Training مع ضبط المعلمات الفائقة.

ما ستتعرّف عليه

ستتعرَّف على كيفية:

  • إنشاء مثيل AI Platform Notebooks
  • إنشاء نموذج PyTorch
  • تدريب النموذج باستخدام ضبط المعلمات الفائقة على AI Platform Training

يبلغ إجمالي تكلفة تشغيل هذا الدرس التطبيقي على Google Cloud حوالي 1 دولار أمريكي.

2. إعداد البيئة

يجب أن يكون لديك مشروع على Google Cloud Platform مع تفعيل الفوترة لتتمكّن من تنفيذ هذا الدرس العملي. لإنشاء مشروع، اتّبِع التعليمات هنا.

الخطوة 1: تفعيل Cloud AI Platform Models API

انتقِل إلى قسم "AI Platform Models" في Cloud Console وانقر على "تفعيل" إذا لم تكن الميزة مفعَّلة بعد.

d0d38662851c6af3.png

الخطوة 2: تفعيل واجهة برمجة التطبيقات Compute Engine API

انتقِل إلى Compute Engine وانقر على تفعيل إذا لم يكن مفعّلاً بعد. يجب توفير هذه المعلومات لإنشاء مثيل دفتر الملاحظات.

الخطوة 3: إنشاء مثيل AI Platform Notebooks

انتقِل إلى قسم "دفاتر ملاحظات AI Platform" في Cloud Console وانقر على إنشاء مثيل جديد. بعد ذلك، اختَر أحدث نوع مثيل PyTorch (بدون وحدات معالجة الرسومات):

892b7588f940d145.png

استخدِم الخيارات التلقائية أو امنحها اسمًا مخصّصًا إذا أردت ذلك، ثم انقر على إنشاء. بعد إنشاء المثيل، انقر على فتح JupyterLab:

63d2cf44801c2df5.png

بعد ذلك، افتح مثيلاً لدفتر ملاحظات Python 3 من مشغّل التطبيقات:

de4c86c6c7f9438f.png

أنت الآن جاهز للبدء.

الخطوة 5: استيراد حِزم Python

في الخلية الأولى من دفتر الملاحظات، أضِف عمليات الاستيراد التالية وشغِّل الخلية. يمكنك تشغيله من خلال الضغط على زر السهم المتّجه لليسار في القائمة العلوية أو الضغط على command-enter:

import datetime
import numpy as np
import os
import pandas as pd
import time

ستلاحظ أنّنا لا نستورد PyTorch هنا. ويرجع ذلك إلى أنّنا ننفّذ مهمة التدريب على AI Platform Training، وليس من مثيل Notebook.

3- إنشاء حزمة لوظيفة التدريب

لتشغيل مهمة التدريب على AI Platform Training، سنحتاج إلى تجميع رمز التدريب محليًا في مثيل Notebooks، وإلى حزمة Cloud Storage لتخزين مواد العرض الخاصة بمهمتنا. أولاً، سننشئ حزمة تخزين. يمكنك تخطّي هذه الخطوة إذا كان لديك حساب حالي.

الخطوة 1: إنشاء حزمة Cloud Storage للنموذج

لنبدأ أولاً بتحديد بعض متغيرات البيئة التي سنستخدمها في بقية الدرس التطبيقي حول الترميز. املأ القيم أدناه باسم مشروعك على السحابة الإلكترونية واسم حزمة التخزين في السحابة الإلكترونية التي تريد إنشاءها (يجب أن يكون الاسم فريدًا على مستوى العالم):

# Update these to your own GCP project, model, and version names
GCP_PROJECT = 'your-gcp-project'
BOCKET_URL = 'gs://storage_bucket_name'

نحن الآن جاهزون لإنشاء حزمة تخزين، وسنشير إليها عند بدء مهمة التدريب.

نفِّذ أمر gsutil التالي من داخل دفتر ملاحظاتك لإنشاء حزمة:

!gsutil mb $BUCKET_URL

الخطوة 2: إنشاء الملفات الأولية لحزمة Python

لتشغيل مهمة تدريب على AI Platform، يجب ضبط إعدادات الرمز البرمجي كحزمة Python. يتألف ذلك من ملف setup.py في دليلنا الجذر يحدّد أي تبعيات حزمة خارجية، ودليل فرعي يحمل اسم الحزمة (سنسمّيه هنا trainer/)، وملف __init__.py فارغ داخل هذا الدليل الفرعي.

أولاً، لنكتب ملف setup.py. نستخدم أوامر iPython السحرية ‎ %%writefile لحفظ الملف في مثيلنا. لقد حدّدنا هنا 3 مكتبات خارجية سنستخدمها في رمز التدريب: PyTorch وScikit-learn وPandas:

%%writefile setup.py
from setuptools import find_packages
from setuptools import setup

REQUIRED_PACKAGES = ['torch>=1.5', 'scikit-learn>=0.20', 'pandas>=1.0']

setup(
    name='trainer',
    version='0.1',
    install_requires=REQUIRED_PACKAGES,
    packages=find_packages(),
    include_package_data=True,
    description='My training application package.'
)

بعد ذلك، لننشئ الدليل trainer/ وملف init.py الفارغ داخله. يستخدم Python هذا الملف للتعرّف على أنّ هذا العنصر هو حزمة:

!mkdir trainer
!touch trainer/__init__.py

نحن الآن جاهزون لبدء إنشاء مهمة التدريب.

4. معاينة مجموعة البيانات

يركّز هذا التمرين العملي على الأدوات اللازمة لتدريب النماذج هنا، ولكن لنلقِ نظرة سريعة على مجموعة البيانات التي سنستخدمها لتدريب نموذجنا على الفهم. سنستخدم مجموعة بيانات المواليد المتوفّرة في BigQuery. يحتوي هذا الملف على بيانات الولادات في الولايات المتحدة على مدى عدة عقود. سنستخدم بضعة أعمدة من مجموعة البيانات لتوقّع وزن الطفل عند الولادة. مجموعة البيانات الأصلية كبيرة جدًا، وسنستخدم مجموعة فرعية منها أتحناها لك في حزمة Cloud Storage.

الخطوة 1: تنزيل مجموعة بيانات المواليد في BigQuery

لننزّل نسخة مجموعة البيانات التي أتحناها لك في Cloud Storage إلى Pandas DataFrame ونستعرضها.

natality = pd.read_csv('https://storage.googleapis.com/ml-design-patterns/natality.csv')
natality.head()

تحتوي مجموعة البيانات هذه على أقل من 100,000 صف. سنستخدم 5 سمات لتوقّع وزن الطفل عند الولادة: عمر الأم والأب، وأسابيع الحمل، والزيادة في وزن الأم بالرطل، وجنس الطفل ممثلاً بقيمة منطقية.

5- تحديد مهمة التدريب مع ضبط المعلَمة الفائقة

سنكتب نص التدريب البرمجي في ملف باسم model.py ضمن الدليل الفرعي trainer/ الذي أنشأناه سابقًا. سيتم تشغيل مهمة التدريب على AI Platform Training، كما ستستفيد من خدمة ضبط المَعلمات الفائقة في AI Platform للعثور على المَعلمات الفائقة المثالية لنموذجنا باستخدام التحسين البايزي.

الخطوة 1: إنشاء نص التدريب

أولاً، لننشئ ملف Python يتضمّن نص التدريب. بعد ذلك، سنحلّل ما يحدث فيه. سيؤدي تنفيذ أمر ‎ %%writefile إلى كتابة رمز النموذج في ملف Python محلي:

%%writefile trainer/model.py
import argparse
import hypertune
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim

from sklearn.utils import shuffle
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import normalize

def get_args():
    """Argument parser.
    Returns:
        Dictionary of arguments.
    """
    parser = argparse.ArgumentParser(description='PyTorch MNIST')
    parser.add_argument('--job-dir',  # handled automatically by AI Platform
                        help='GCS location to write checkpoints and export ' \
                             'models')
    parser.add_argument('--lr',  # Specified in the config file
                        type=float,
                        default=0.01,
                        help='learning rate (default: 0.01)')
    parser.add_argument('--momentum',  # Specified in the config file
                        type=float,
                        default=0.5,
                        help='SGD momentum (default: 0.5)')
    parser.add_argument('--hidden-layer-size',  # Specified in the config file
                        type=int,
                        default=8,
                        help='hidden layer size')
    args = parser.parse_args()
    return args

def train_model(args):
    # Get the data
    natality = pd.read_csv('https://storage.googleapis.com/ml-design-patterns/natality.csv')
    natality = natality.dropna()
    natality = shuffle(natality, random_state = 2)
    natality.head()

    natality_labels = natality['weight_pounds']
    natality = natality.drop(columns=['weight_pounds'])


    train_size = int(len(natality) * 0.8)
    traindata_natality = natality[:train_size]
    trainlabels_natality = natality_labels[:train_size]

    testdata_natality = natality[train_size:]
    testlabels_natality = natality_labels[train_size:]

    # Normalize and convert to PT tensors
    normalized_train = normalize(np.array(traindata_natality.values), axis=0)
    normalized_test = normalize(np.array(testdata_natality.values), axis=0)

    train_x = torch.Tensor(normalized_train)
    train_y = torch.Tensor(np.array(trainlabels_natality))

    test_x = torch.Tensor(normalized_test)
    test_y = torch.Tensor(np.array(testlabels_natality))

    # Define our data loaders
    train_dataset = torch.utils.data.TensorDataset(train_x, train_y)
    train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=128, shuffle=True)

    test_dataset = torch.utils.data.TensorDataset(test_x, test_y)
    test_dataloader = torch.utils.data.DataLoader(test_dataset, batch_size=128, shuffle=False)

    # Define the model, while tuning the size of our hidden layer
    model = nn.Sequential(nn.Linear(len(train_x[0]), args.hidden_layer_size),
                          nn.ReLU(),
                          nn.Linear(args.hidden_layer_size, 1))
    criterion = nn.MSELoss()

    # Tune hyperparameters in our optimizer
    optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)
    epochs = 20
    for e in range(epochs):
        for batch_id, (data, label) in enumerate(train_dataloader):
            optimizer.zero_grad()
            y_pred = model(data)
            label = label.view(-1,1)
            loss = criterion(y_pred, label)
            
            loss.backward()
            optimizer.step()


    val_mse = 0
    num_batches = 0
    # Evaluate accuracy on our test set
    with torch.no_grad():
        for i, (data, label) in enumerate(test_dataloader):
            num_batches += 1
            y_pred = model(data)
            mse = criterion(y_pred, label.view(-1,1))
            val_mse += mse.item()


    avg_val_mse = (val_mse / num_batches)

    # Report the metric we're optimizing for to AI Platform's HyperTune service
    # In this example, we're mimizing error on our test set
    hpt = hypertune.HyperTune()
    hpt.report_hyperparameter_tuning_metric(
        hyperparameter_metric_tag='val_mse',
        metric_value=avg_val_mse,
        global_step=epochs        
    )

def main():
    args = get_args()
    print('in main', args)
    train_model(args)

if __name__ == '__main__':
    main()

تتألف مهمة التدريب من وظيفتَين يتم فيهما تنفيذ معظم العمل.

  • get_args(): يؤدي هذا الرمز إلى تحليل وسيطات سطر الأوامر التي سنمرّرها عند إنشاء مهمة التدريب، بالإضافة إلى المعلمات الفائقة التي نريد أن تحسّنها خدمة AI Platform. في هذا المثال، تتضمّن قائمة الوسيطات فقط المعلمات الفائقة التي سنعمل على تحسينها، وهي معدّل التعلّم والزخم وعدد الخلايا العصبية في الطبقة المخفية للنموذج.
  • train_model(): هنا، ننزّل البيانات إلى Pandas DataFrame، ونحوّلها إلى تنسيق عادي، ثم نحولها إلى PyTorch Tensors، وبعد ذلك نحدّد النموذج. لإنشاء نموذجنا، نستخدم واجهة برمجة التطبيقات nn.Sequential PyTorch، التي تتيح لنا تحديد نموذجنا كمجموعة من الطبقات:
model = nn.Sequential(nn.Linear(len(train_x[0]), args.hidden_layer_size),
                      nn.ReLU(),
                      nn.Linear(args.hidden_layer_size, 1))

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

الخطوة 2: استخدام خدمة ضبط المعلمات الفائقة في AI Platform

بدلاً من تجربة قيم مختلفة للمعلمات الفائقة وإعادة تدريب النموذج في كل مرة يدويًا، سنستخدم خدمة تحسين المعلمات الفائقة في Cloud AI Platform. إذا أعددنا مهمة التدريب باستخدام وسيطات المعلَمات الفائقة، ستستخدم خدمة AI Platform التحسين البايزي للعثور على القيم المثالية للمعلَمات الفائقة التي نحدّدها.

في عملية ضبط المعلَمات الفائقة، تتألف التجربة الواحدة من عملية تدريب واحدة لنموذجنا باستخدام مجموعة محدّدة من قيم المعلَمات الفائقة. استنادًا إلى عدد التجارب التي نجريها، ستستخدم "منصة الذكاء الاصطناعي" نتائج التجارب المكتملة لتحسين المعلمات الفائقة التي تختارها للتجارب المستقبلية. لضبط عملية تحسين المعلمات الفائقة، علينا تمرير ملف إعداد عند بدء مهمة التدريب مع بعض البيانات حول كل من المعلمات الفائقة التي نعمل على تحسينها.

بعد ذلك، أنشئ ملف الإعداد هذا على جهازك:

%%writefile config.yaml
trainingInput:
  hyperparameters:
    goal: MINIMIZE
    maxTrials: 10
    maxParallelTrials: 5
    hyperparameterMetricTag: val_mse
    enableTrialEarlyStopping: TRUE
    params:
    - parameterName: lr
      type: DOUBLE
      minValue: 0.0001
      maxValue: 0.1
      scaleType: UNIT_LINEAR_SCALE
    - parameterName: momentum
      type: DOUBLE
      minValue: 0.0
      maxValue: 1.0
      scaleType: UNIT_LINEAR_SCALE
    - parameterName: hidden-layer-size
      type: INTEGER
      minValue: 8
      maxValue: 32
      scaleType: UNIT_LINEAR_SCALE

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

في بداية مهمة التحسين، نحدّد أيضًا المقياس الذي نريد تحسينه. لاحظ أنّه في نهاية الدالة train_model() أعلاه، نرسل هذا المقياس إلى AI Platform في كل مرة يكتمل فيها إصدار تجريبي. في هذه الحالة، نعمل على تقليل متوسط الخطأ التربيعي في نموذجنا، لذا نريد استخدام مُدخلات الضبط التي تؤدي إلى أقل متوسط خطأ تربيعي لنموذجنا. يتطابق اسم هذا المقياس (val_mse) مع الاسم الذي نستخدمه لإعداد تقرير عنه عندما نتصل بـ report_hyperparameter_tuning_metric() في نهاية الفترة التجريبية.

6. تشغيل مهمة تدريب على AI Platform

في هذا القسم، سنبدأ مهمة تدريب النموذج من خلال ضبط المعلمات الفائقة على AI Platform.

الخطوة 1: تحديد بعض متغيّرات البيئة

لنحدّد أولاً بعض متغيرات البيئة التي سنستخدمها لبدء مهمة التدريب. إذا كنت تريد تشغيل مهمتك في منطقة مختلفة، عدِّل المتغيّر REGION أدناه:

MAIN_TRAINER_MODULE = "trainer.model"
TRAIN_DIR = os.getcwd() + '/trainer'
JOB_DIR = BUCKET_URL + '/output'
REGION = "us-central1"

يجب أن يكون لكل مهمة تدريب على "منصة الذكاء الاصطناعي" اسم فريد. نفِّذ ما يلي لتحديد متغيّر لاسم مهمتك باستخدام طابع زمني:

timestamp = str(datetime.datetime.now().time())
JOB_NAME = 'caip_training_' + str(int(time.time()))

الخطوة 2: بدء مهمة التدريب

سننشئ مهمة التدريب باستخدام gcloud، وهي واجهة سطر الأوامر (CLI) في Google Cloud. يمكننا تشغيل هذا الأمر مباشرةً في دفتر الملاحظات، مع الإشارة إلى المتغيرات التي حدّدناها أعلاه:

!gcloud ai-platform jobs submit training $JOB_NAME \
        --scale-tier basic \
        --package-path $TRAIN_DIR \
        --module-name $MAIN_TRAINER_MODULE \
        --job-dir $JOB_DIR \
        --region $REGION \
        --runtime-version 2.1 \
        --python-version 3.7 \
        --config config.yaml

إذا تم إنشاء مهمتك بشكل صحيح، انتقِل إلى قسم المهام في وحدة تحكّم AI Platform لمراقبة السجلات.

الخطوة 3: مراقبة مهمتك

بعد الانتقال إلى قسم "الوظائف" في وحدة التحكّم، انقر على الوظيفة التي بدأت للتو للاطّلاع على التفاصيل:

c184167641bb7ed7.png

عند بدء الجولة الأولى من التجارب، ستتمكّن من الاطّلاع على قيم المَعلمات الفائقة التي تم اختيارها لكل تجربة:

787c053ef9110e6b.png

عند اكتمال التجارب، سيتم تسجيل القيمة الناتجة لمقياس التحسين (val_mse في هذه الحالة) هنا. من المفترض أن تستغرق المهمة من 15 إلى 20 دقيقة، وستبدو لوحة البيانات على النحو التالي عند انتهاء المهمة (ستختلف القيم الدقيقة):

47ef6b9b4ecb532c.png

لتصحيح الأخطاء المحتملة ومراقبة مهمتك بتفصيل أكبر، انقر على عرض السجلات من صفحة تفاصيل المهام:

18c32dcd36351930.png

سيظهر هنا كل بيان print() في رمز تدريب النموذج. إذا واجهت مشاكل، جرِّب إضافة المزيد من عبارات الطباعة وبدء مهمة تدريب جديدة.

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

7. تنظيف

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

879147427150b6c7.png

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

باستخدام قائمة "التنقّل" في Cloud Console، انتقِل إلى "مساحة التخزين" واحذف الحزمتَين اللتين أنشأتهما لتخزين مواد عرض النموذج.