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

1. نظرة عامة

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

المعلومات التي تطّلع عليها

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

  • إنشاء مثيل من AI Platform Notebook
  • إنشاء نموذج PyTorch
  • تدريب النموذج من خلال ضبط المعلَمة الفائقة في "التدريب على منصة الذكاء الاصطناعي"

تبلغ التكلفة الإجمالية لتشغيل هذا التمرين على Google Cloud حوالي $1.

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

ستحتاج إلى مشروع Google Cloud Platform مع تفعيل الفوترة لتشغيل هذا الدرس التطبيقي حول الترميز. لإنشاء مشروع، يُرجى اتّباع التعليمات هنا.

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

انتقِل إلى قسم "نماذج منصة الذكاء الاصطناعي" (AI) في Cloud Console وانقر على "تفعيل" إذا لم يسبق لك تفعيله.

d0d38662851c6af3.png

الخطوة 2: تفعيل Compute Engine API

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

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

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

892b7588f940d145.png

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

63d2cf44801c2df5.png

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

de4c86c6c7f9438f.png

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

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

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

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

ستلاحظ أنّنا لا نستورد PyTorch هنا. ويرجع ذلك إلى أنّنا نُجري المهمة التدريبية بشأن "التدريب على منصة الذكاء الاصطناعي"، وليس من مثيل تطبيق Notebook.

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

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

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

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

# 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.'
)

لنبدأ بعد ذلك بإنشاء دليلنا "المدرّب" وملف init.py الفارغ بداخله. تستخدم بايثون هذا الملف لتعرف أن هذه حزمة:

!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()

تحتوي مجموعة البيانات هذه على أقل بقليل من 100000 صف. سوف نستخدم 5 ميزات للتنبؤ بوزن الطفل عند الولادة، وهي: عمر الأم والأب، وأسابيع الحمل، وزيادة وزن الأم بالرطل، وجنس الجنين كقيمة منطقية.

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

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

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

لنبدأ أولًا بإنشاء ملف بايثون باستخدام البرنامج النصي الخاص بنا للتدريب. ثم سنحلل ما يحدث فيها. سيؤدي تشغيل الأمر %%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، ثم نحدِّد النموذج. لإنشاء نموذجنا، نستخدم واجهة برمجة تطبيقات PyTorch nn.Sequential، التي تتيح لنا تعريف النموذج كحزمة من الطبقات:
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) تحسين Bayesian للعثور على القيم المثالية لفرط المعلمات التي نحدّدها.

في ضبط المعلَمة الفائقة، تتألف تجربة فردية من عملية تدريب واحدة لنموذجنا مع مجموعة محدّدة من قيم المعلَمة الفائقة. استنادًا إلى عدد التجارب التي نجريها، ستستخدِم 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"

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

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

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

سننشئ مهمة التدريب باستخدام gcloud، واجهة سطر الأوامر في 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، انتقِل إلى "مساحة التخزين" واحذف كلتا المجموعتَين اللتين أنشأتهما لتخزين مواد عرض نموذجك.