تدريب نموذج التراجع باستخدام AutoML وPython (SDK v1)

ينطبق على:Python SDK azureml v1

في هذه المقالة، تتعلم كيفية تدريب نموذج التراجع باستخدام Azure Machine Learning Python SDK باستخدام التعلم الآلي التلقائي من Azure Machine Learning. يتوقع نموذج الانحدار هذا أسعار سيارات الأجرة في مدينة نيويورك.

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

Flow diagram

يمكنك كتابة التعليمات البرمجية باستخدام Python SDK في هذه المقالة. ستتعلم المهام التالية:

  • تنزيل البيانات وتحويلها وتنقيتها باستخدام Azure Open Datasets.
  • تدريب نموذج انحدار التعلم الآلي التلقائي.
  • حساب دقة الطراز.

للحصول على AutoML بدون تعليمة برمجية، جرب البرامج التعليمية التالية:

المتطلبات الأساسية

في حال لم يكن لديك اشتراك Azure، فأنشئ حساباً مجانيّاً قبل البدء. جرب الإصدار المجاني أو المدفوع الخاص بـ Azure Machine Learning اليوم.

  • استكمل البدء السريع: ابدأ باستخدام Azure Machine Learning إذا لم تكن لديك مساحة عمل Azure Machine Learning أو مثيل حساب.
  • بعد إكمال التشغيل السريع:
    1. حدد Notebooks في studio.
    2. حدد علامة التبويب Samples.
    3. افتح دفتر ملاحظات SDK v1/tutorials/regression-automl-nyc-taxi-data/regression-automated-ml.ipynb .
    4. لتشغيل كل خلية في البرنامج التعليمي، حدد استنساخ دفتر الملاحظات هذا

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

  • ثبّت automl العميل الكامل.
  • شغل pip install azureml-opendatasets azureml-widgets للحصول على الحزم المطلوبة.

اشرع في تنزيل وإعداد البيانات.

استورد الحزم الضرورية. تحتوي حزمة Open Datasets على فئة تمثل كل مصدر بيانات (NycTlcGreen على سبيل المثال) لتصفية معلمات المحفوظات بسهولة قبل التنزيل.

from azureml.opendatasets import NycTlcGreen
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta

ابدأ بإنشاء إطار بيانات للاحتفاظ ببيانات سيارة الأجرة. عند العمل في بيئة غير Spark، يسمح Open Datasets فقط بتنزيل شهر واحد من البيانات في كل مرة مع فئات معينة لتجنبها MemoryError مع مجموعات البيانات الكبيرة.

لتنزيل بيانات سيارات الأجرة، كرر استحضار شهر واحد في وقت واحد، وقبل إلحاقه بـ green_taxi_df عينة عشوائية مكونة من 2000 سجل من كل شهر لتجنب انتفاخ إطار البيانات. ثم باشر معاينة البيانات.

green_taxi_df = pd.DataFrame([])
start = datetime.strptime("1/1/2015","%m/%d/%Y")
end = datetime.strptime("1/31/2015","%m/%d/%Y")

for sample_month in range(12):
    temp_df_green = NycTlcGreen(start + relativedelta(months=sample_month), end + relativedelta(months=sample_month)) \
        .to_pandas_dataframe()
    green_taxi_df = green_taxi_df.append(temp_df_green.sample(2000))

green_taxi_df.head(10)
رقم المورد رمز lpep لتاريخ ووقت الركوب وقت وتاريخ الوصول ورمز lpep passengerCount tripDistance puLocationId doLocationId pickupLongitude إحداثيات خط طول الركوب dropoffLongitude ... نوع الدفع مقدار الأجرة extra mtaTax تكلفة رسوم التحسين مقدار الإكرامية المبلغ ehailFee totalAmount tripType
131969 2 2015-01-11 05:34:44 2015-01-11 05:45:03 3 4.84 بلا بلا -73.88 40.84 -73.94 ... 2 15.00 0.50 0.50 0.3 0.00 0.00 nan 16.30
1129817 2 2015-01-20 16:26:29 2015-01-20 16:30:26 1 0.69 بلا بلا -73.96 40.81 -73.96 ... 2 4.50 1.00 0.50 0.3 0.00 0.00 nan 6.30
1278620 2 2015-01-01 05:58:10 2015-01-01 06:00:55 1 0.45 بلا بلا -73.92 40.76 -73.91 ... 2 4.00 0.00 0.50 0.3 0.00 0.00 nan 4.80
348430 2 2015-01-17 02:20:50 2015-01-17 02:41:38 1 0.00 بلا بلا -73.81 40.70 -73.82 ... 2 12.50 0.50 0.50 0.3 0.00 0.00 nan 13.80
1269627 1 2015-01-01 05:04:10 2015-01-01 05:06:23 1 0.50 بلا بلا -73.92 40.76 -73.92 ... 2 4.00 0.50 0.50 0 0.00 0.00 nan 5.00
811755 1 2015-01-04 19:57:51 2015-01-04 20:05:45 2 1.10 بلا بلا -73.96 40.72 -73.95 ... 2 6.50 0.50 0.50 0.3 0.00 0.00 nan 7.80
737281 1 2015-01-03 12:27:31 2015-01-03 12:33:52 1 0.90 بلا بلا -73.88 40.76 -73.87 ... 2 6.00 0.00 0.50 0.3 0.00 0.00 nan 6.80
113951 1 2015-01-09 23:25:51 2015-01-09 23:39:52 1 3.30 بلا بلا -73.96 40.72 -73.91 ... 2 12.50 0.50 0.50 0.3 0.00 0.00 nan 13.80
150436 2 2015-01-11 17:15:14 2015-01-11 17:22:57 1 1.19 بلا بلا -73.94 40.71 -73.95 ... 1 7.00 0.00 0.50 0.3 1.75 0.00 nan 9.55
432136 2 2015-01-22 23:16:33 2015-01-22 23:20:13 1 0.65 بلا بلا -73.94 40.71 -73.94 ... 2 5.00 0.50 0.50 0.3 0.00 0.00 nan 6.30

قم بإزالة بعض الأعمدة التي لن تحتاجها للتدريب أو إنشاء ميزات أخرى. أتمتة التعلم الآلي ستتعامل تلقائيا مع الميزات المستندة إلى الوقت مثل lpepPickupDatetime.

columns_to_remove = ["lpepDropoffDatetime", "puLocationId", "doLocationId", "extra", "mtaTax",
                     "improvementSurcharge", "tollsAmount", "ehailFee", "tripType", "rateCodeID",
                     "storeAndFwdFlag", "paymentType", "fareAmount", "tipAmount"
                    ]
for col in columns_to_remove:
    green_taxi_df.pop(col)

green_taxi_df.head(5)

باشر تنقية البيانات

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

green_taxi_df.describe()
رقم المورد passengerCount tripDistance pickupLongitude إحداثيات خط طول الركوب dropoffLongitude dropoffLatitude totalAmount month_num day_of_month day_of_week hour_of_day
عدد 48000.00 48000.00 48000.00 48000.00 48000.00 48000.00 48000.00 48000.00 48000.00 48000.00
المتوسط 1.78 1.37 2.87 -73.83 40.69 -73.84 40.70 14.75 6.50 15.13
المعيار 0.41 1.04 2.93 2.76 1.52 2.61 1.44 12.08 3.45 8.45
دقيقة ١.٠٠ 0.00 0.00 -74.66 0.00 -74.66 0.00 -300.00 ١.٠٠ ١.٠٠
25% 2.00 1.00 1.06 -73.96 40.70 -73.97 40.70 7.80 3.75 8.00
50% 2.00 1.00 1.90 -73.94 40.75 -73.94 40.75 11.30 6.50 15.00
75% 2.00 1.00 3.60 -73.92 40.80 -73.91 40.79 17.80 9.25 22.00
max 2.00 9.00 97.57 0.00 41.93 0.00 41.94 450.00 12.00 30.00

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

وبالإضافة إلى ذلك، اشرع في تصفية الحقل tripDistance لتكون قيمته أكبر من صفر وأقل من 31 ميلاً (مسافة نصف السهم بين زوجَين من خطوط العرض/خطوط الطول). وهذا يلغي الرحلات الطويلة ذات القيم الخارجية التي لها تكلفة رحلة غير متناسقة.

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

اشرع في تصفية هذه الحالات الشاذة باستخدام دالات الاستعلام، ثم أزل الأعمدة القليلة الأخيرة غير الضرورية للتدريب.

final_df = green_taxi_df.query("pickupLatitude>=40.53 and pickupLatitude<=40.88")
final_df = final_df.query("pickupLongitude>=-74.09 and pickupLongitude<=-73.72")
final_df = final_df.query("tripDistance>=0.25 and tripDistance<31")
final_df = final_df.query("passengerCount>0 and totalAmount>0")

columns_to_remove_for_training = ["pickupLongitude", "pickupLatitude", "dropoffLongitude", "dropoffLatitude"]
for col in columns_to_remove_for_training:
    final_df.pop(col)

اتصل بـ describe() مرة أخرى بشأن البيانات لضمان عمل التنقية على النحو المتوقع. لديك الآن مجموعة معدة ومنقاة من بيانات سيارات الأجرة والعطلات والطقس لاستخدامها في تدريب نموذج التعلم الآلي.

final_df.describe()

تكوين مساحة العمل

أنشئ كائن مساحة عمل من مساحة العمل الموجودة. تُمثل مساحة العمل فئة تقبل اشتراك Azure الخاص بك ومعلومات المورد الخاصة بك. كما أنها تنشئ موردًا سحابيًّا لمراقبة وتتبع تشغيل النموذج الخاص بك. Workspace.from_config() يقرأ الملف config.json، ويحمل تفاصيل المصادقة في كائن اسمه ws. يُستخدم ws في جميع التعليمات البرمجية الباقية في هذه المقالة.

from azureml.core.workspace import Workspace
ws = Workspace.from_config()

تقسيم البيانات إلى مجموعات تدريب واختبار

قسِّم البيانات إلى مجموعات تدريب واختبار باستخدام الدالة train_test_split في المكتبة scikit-learn. هذه الدالة تفصل البيانات إلى «س» (ميزات) مجموعة بيانات لتدريب النموذج، و«ص» (القيم التي سيتم التنبؤ بها) مجموعة بيانات للاختبار.

تحدد المعلمة test_sizeالنسبة المئوية للبيانات التي سيتم تخصيصها للاختبار. تعين المعلمة random_state مولدًا عشوائيًّا بحيث تكون تقسيمات اختبار التدريب حتمية.

from sklearn.model_selection import train_test_split

x_train, x_test = train_test_split(final_df, test_size=0.2, random_state=223)

الغرض من هذه الخطوة هو الحصول على نقاط بيانات لاختبار النموذج النهائي الذي لم يتم استخدامه لتدريب النموذج من أجل قياس الدقة الحقيقية.

وبعبارة أخرى، ينبغي أن يكون النموذج المدرب تدريبًا جيدًا قادرًا على إجراء تنبؤات دقيقة من بيانات لم يرها بالفعل. لديك الآن بيانات معدة للتدريب التلقائي لنموذج التعلم الآلي.

تدريب نموذج تلقائيًّا

لتدريب نموذج تلقائيًّا، يجب اتخاذ الخطوات التالية:

  1. حدد إعدادات تشغيل التجربة. أرفق بيانات التدريب بالتكوين، وعدل الإعدادات التي تتحكم في عملية التدريب.
  2. أرسل التجربة لضبط الطراز. بعد تقديم التجربة، يتكرر العمل من خلال خوارزميات التعلم الآلي المختلفة وإعدادات مقياس فرط المقاييس، مع الالتزام بالقيود المحددة. إنه يختار النموذج الأنسب من خلال تحسين مقياس الدقة.

تحديد إعدادات التدريب

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

الخاصية القيمة في هذه المقالة ‏‏الوصف
iteration_timeout_minutes 10 الحد الزمني بالدقائق لكل تكرار. زد هذه القيمة لمجموعات البيانات الكبيرة التي تحتاج إلى مزيد من الوقت لكل تكرار.
experiment_timeout_hours 0.3 الحد الأقصى للوقت بالساعات الذي يمكن أن تستغرقه جميع التكرارات المجمعة قبل إنهاء التجربة.
enable_early_stopping صواب ضع علامة لتمكين الإنهاء المبكر إذا لم تتحسن النتيجة على المدى القصير.
primary_metric spearman_correlation المقياس الذي تريد تحسينه. يتم اختيار النموذج الأنسب بناء على هذا المقياس.
featurization auto باستخدام auto، يمكن أن تعالج التجربة مسبقًا بيانات الإدخال (التعامل مع البيانات المفقودة، وتحويل النص إلى أرقام، وغير ذلك).
verbosity logging.INFO التحكم في مستوى تسجيل الدخول.
n_cross_validations 5 عدد تقسيمات التحقق من الصحة المتقاطعة التي يجب إجراؤها عند عدم تحديد بيانات التحقق من الصحة.
import logging

automl_settings = {
    "iteration_timeout_minutes": 10,
    "experiment_timeout_hours": 0.3,
    "enable_early_stopping": True,
    "primary_metric": 'spearman_correlation',
    "featurization": 'auto',
    "verbosity": logging.INFO,
    "n_cross_validations": 5
}

مرر إعدادات التدريب المحددة بوصفها معلمة **kwargs للكائن AutoMLConfig. إضافة إلى ذلك، حدد بيانات التدريب ونوع النموذج، الذي يتمثل في regression في هذه الحالة.

from azureml.train.automl import AutoMLConfig

automl_config = AutoMLConfig(task='regression',
                             debug_log='automated_ml_errors.log',
                             training_data=x_train,
                             label_column_name="totalAmount",
                             **automl_settings)

إشعار

تصبح خطوات المعالجة المسبقة للتعلم الآلي التلقائي (يضم التطبيع، والتعامل مع البيانات المفقودة، وتحويل النصوص إلى أرقام، وغير ذلك) جزءًا من النموذج الأساسي. عند استخدام النموذج للتنبؤات، يتم تطبيق خطوات المعالجة المسبقة ذاتها المطبقة في أثناء التدريب على بيانات الإدخال الخاصة بك تلقائيًّا.

تدريب نموذج الانحدار التلقائي

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

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

from azureml.core.experiment import Experiment
experiment = Experiment(ws, "Tutorial-NYCTaxi")
local_run = experiment.submit(automl_config, show_output=True)
Running on local machine
Parent Run ID: AutoML_1766cdf7-56cf-4b28-a340-c4aeee15b12b
Current status: DatasetFeaturization. Beginning to featurize the dataset.
Current status: DatasetEvaluation. Gathering dataset statistics.
Current status: FeaturesGeneration. Generating features for the dataset.
Current status: DatasetFeaturizationCompleted. Completed featurizing the dataset.
Current status: DatasetCrossValidationSplit. Generating individually featurized CV splits.
Current status: ModelSelection. Beginning model selection.

****************************************************************************************************
ITERATION: The iteration being evaluated.
PIPELINE: A summary description of the pipeline being evaluated.
DURATION: Time taken for the current iteration.
METRIC: The result of computing score on the fitted pipeline.
BEST: The best observed score thus far.
****************************************************************************************************

 ITERATION   PIPELINE                                       DURATION      METRIC      BEST
         0   StandardScalerWrapper RandomForest             0:00:16       0.8746    0.8746
         1   MinMaxScaler RandomForest                      0:00:15       0.9468    0.9468
         2   StandardScalerWrapper ExtremeRandomTrees       0:00:09       0.9303    0.9468
         3   StandardScalerWrapper LightGBM                 0:00:10       0.9424    0.9468
         4   RobustScaler DecisionTree                      0:00:09       0.9449    0.9468
         5   StandardScalerWrapper LassoLars                0:00:09       0.9440    0.9468
         6   StandardScalerWrapper LightGBM                 0:00:10       0.9282    0.9468
         7   StandardScalerWrapper RandomForest             0:00:12       0.8946    0.9468
         8   StandardScalerWrapper LassoLars                0:00:16       0.9439    0.9468
         9   MinMaxScaler ExtremeRandomTrees                0:00:35       0.9199    0.9468
        10   RobustScaler ExtremeRandomTrees                0:00:19       0.9411    0.9468
        11   StandardScalerWrapper ExtremeRandomTrees       0:00:13       0.9077    0.9468
        12   StandardScalerWrapper LassoLars                0:00:15       0.9433    0.9468
        13   MinMaxScaler ExtremeRandomTrees                0:00:14       0.9186    0.9468
        14   RobustScaler RandomForest                      0:00:10       0.8810    0.9468
        15   StandardScalerWrapper LassoLars                0:00:55       0.9433    0.9468
        16   StandardScalerWrapper ExtremeRandomTrees       0:00:13       0.9026    0.9468
        17   StandardScalerWrapper RandomForest             0:00:13       0.9140    0.9468
        18   VotingEnsemble                                 0:00:23       0.9471    0.9471
        19   StackEnsemble                                  0:00:27       0.9463    0.9471

استكشاف النتائج.

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

from azureml.widgets import RunDetails
RunDetails(local_run).show()

Jupyter widget run detailsJupyter widget plot

استرداد أفضل نموذج

حدد أفضل طراز للتكرارات الخاصة بك. ترجع الدالة get_output أفضل نموذج من حيث التشغيل والمواءمة لآخر عملية استدعاء ملائمة. عن طريق استخدام الأحمال في get_output، يمكنك استرداد أفضل نموذج من حيث التشغيل والمواءمة لأي مقياس مُسجل أو لتكرار محدد.

best_run, fitted_model = local_run.get_output()
print(best_run)
print(fitted_model)

اختبار دقة النموذج

استخدم أفضل طراز لتشغيل التنبؤات في مجموعة بيانات الاختبار للتنبؤ بأجور سيارات الأجرة. تستخدم الدالة predict أفضل نموذج، وتتنبأ بقيم «ص»؛ أي مبلغ الأجرة من مجموعة البيانات x_test. اطبع قيم التكلفة المتوقعة العشر الأولى من y_predict.

y_test = x_test.pop("totalAmount")

y_predict = fitted_model.predict(x_test)
print(y_predict[:10])

احسب root mean squared error للنتائج. حوّل إطار البيانات y_test إلى قائمة لمقارنتها بالقيم المتوقعة. تأخذ الدالة mean_squared_error مصفوفتَين من القيم، وتحسب متوسط ​​الخطأ التربيعي بينهما. أخذ الجذر التربيعي للنتيجة يعطي خطأ في وحدات المتغير «ص» نفسها؛ أي التكلفة. وهو يشير تقريبًا إلى أي مدى تعد تنبؤات أجرة سيارة الأجرة ضمن الأسعار الفعلية.

from sklearn.metrics import mean_squared_error
from math import sqrt

y_actual = y_test.values.flatten().tolist()
rmse = sqrt(mean_squared_error(y_actual, y_predict))
rmse

شغل التعليمة البرمجية التالية لحساب متوسط خطأ النسبة المئوية المطلقة (MAPE) باستخدام مجموعتَي البيانات y_actual وy_predict الكاملتَين. يحسب هذا المقياس الفرق المطلق بين كل قيمة متوقعة وقيمة فعلية، ويخصم كل الفروقات. بعد ذلك، يعبر عن هذا المجموع بنسبة مئوية من إجمالي القيم الفعلية.

sum_actuals = sum_errors = 0

for actual_val, predict_val in zip(y_actual, y_predict):
    abs_error = actual_val - predict_val
    if abs_error < 0:
        abs_error = abs_error * -1

    sum_errors = sum_errors + abs_error
    sum_actuals = sum_actuals + actual_val

mean_abs_percent_error = sum_errors / sum_actuals
print("Model MAPE:")
print(mean_abs_percent_error)
print()
print("Model Accuracy:")
print(1 - mean_abs_percent_error)
Model MAPE:
0.14353867606052823

Model Accuracy:
0.8564613239394718

من اثنَين من مقاييس دقة التنبؤ، ترى أن النموذج جيد إلى حد ما في التنبؤ بأسعار سيارات الأجرة من ميزات مجموعة البيانات، ويكون ذلك عادةً داخل +- 4.00 دولارات، وحوالي نسبة خطأ قدرها 15%.

إن عملية تطوير نموذج التعلم الآلي التقليدية تتطلب موارد كثيفة للغاية، وتتطلب معرفة كبيرة بالمجال واستثمارًا زمنيًّا لتشغيل ومقارنة نتائج عشرات النماذج. يعد استخدام التعلم الآلي التلقائي وسيلة رائعة لاختبار نماذج مختلفة كثيرة للسيناريو الخاص بك سريعًا.

تنظيف الموارد

لا تكمل هذا القسم إذا كنت تخطط لتشغيل برامج تعليمية أخرى التعلم الآلي Azure.

إيقاف مثيل الحساب

إذا استخدمت مثيل حساب، فقم بإيقاف الجهاز الظاهري عندما لا تستخدمه لتقليل التكلفة.

  1. في مساحة العمل، حدد Compute.

  2. من القائمة، حدد اسم مثيل الحساب.

  3. حدد إيقاف.

  4. عندما تكون جاهزًا لاستخدام الخادم مرة أخرى، حدد Start.

حذف كل شيء

إذا كنت لا تخطط لاستخدام الموارد التي أنشأتها، فاحذفها، حتى لا تتحمل أي رسوم.

  1. من مدخل Microsoft Azure، حدد Resource groups من أقصى الجانب الأيمن.
  2. من القائمة، حدد مجموعة الموارد التي أنشأتها.
  3. حدد Delete resource group.
  4. أدخل اسم مجموعة الموارد. ثم حدد حذف.

يمكنك أيضًا الاحتفاظ بمجموعة الموارد، ولكن حذف مساحة عمل واحدة. اعرض خصائص مساحة العمل، وحدد Delete.

الخطوات التالية

في هذه المقالة للتعلم الآلي التلقائي، أتممت المهام التالية:

  • تكوين مساحة عمل وإعداد بيانات لتجربة.
  • التدريب محليًّا باستخدام نموذج انحدار تلقائي بمعلمات مخصصة.
  • استكشاف ومراجعة نتائج التدريب.

إعداد AutoML لتدريب نماذج رؤية الكمبيوتر باستخدام Python (v1)