نماذج التدريب

يفترض هذا الدليل أنك قد قرأت بالفعل دليل النماذج والطبقات .

في TensorFlow.js هناك طريقتان لتدريب نموذج التعلم الآلي:

  1. باستخدام واجهة برمجة تطبيقات الطبقات مع LayersModel.fit() أو LayersModel.fitDataset() .
  2. باستخدام واجهة برمجة التطبيقات الأساسية مع Optimizer.minimize() .

أولاً، سنلقي نظرة على Layers API، وهي واجهة برمجة تطبيقات عالية المستوى لبناء النماذج والتدريب عليها. بعد ذلك، سنوضح كيفية تدريب نفس النموذج باستخدام Core API.

مقدمة

نموذج التعلم الآلي عبارة عن وظيفة ذات معلمات قابلة للتعلم تقوم بتعيين المدخلات إلى المخرجات المطلوبة. يتم الحصول على المعلمات المثالية من خلال تدريب النموذج على البيانات.

يتضمن التدريب عدة خطوات:

  • الحصول على دفعة من البيانات إلى النموذج.
  • مطالبة النموذج بالتنبؤ.
  • مقارنة هذا التنبؤ بالقيمة "الحقيقية".
  • تحديد مقدار تغيير كل معلمة حتى يتمكن النموذج من التنبؤ بشكل أفضل في المستقبل لتلك الدفعة.

سيوفر النموذج المدرب جيدًا تخطيطًا دقيقًا من المدخلات إلى المخرجات المطلوبة.

المعلمات النموذجية

لنحدد نموذجًا بسيطًا مكونًا من طبقتين باستخدام واجهة برمجة التطبيقات للطبقات:

const model = tf.sequential({
 layers: [
   tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
   tf.layers.dense({units: 10, activation: 'softmax'}),
 ]
});

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

model.weights.forEach(w => {
 console.log(w.name, w.shape);
});

نحصل على الإخراج التالي:

> dense_Dense1/kernel [784, 32]
> dense_Dense1/bias [32]
> dense_Dense2/kernel [32, 10]
> dense_Dense2/bias [10]

هناك 4 أوزان إجمالاً، 2 لكل طبقة كثيفة. هذا متوقع لأن الطبقات الكثيفة تمثل دالة تقوم بتعيين موتر الإدخال x إلى موتر الإخراج y عبر المعادلة y = Ax + b حيث A (النواة) و b (التحيز) هي معلمات الطبقة الكثيفة.

ملاحظة: تتضمن الطبقات الكثيفة بشكل افتراضي انحيازًا، ولكن يمكنك استبعاده عن طريق تحديد {useBias: false} في الخيارات عند إنشاء طبقة كثيفة.

يعد model.summary() طريقة مفيدة إذا كنت تريد الحصول على نظرة عامة على النموذج الخاص بك ومعرفة العدد الإجمالي للمعلمات:

الطبقة (النوع) شكل الإخراج المعلمة #
كثيفة_Dense1 (كثيفة) [خالية، 32] 25120
كثيف_Dense2 (كثيف) [خالية، 10] 330
إجمالي المعلمات: 25450
المعلمات القابلة للتدريب: 25450
المعلمات غير القابلة للتدريب: 0

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

model.weights.forEach(w => {
  const newVals = tf.randomNormal(w.shape);
  // w.val is an instance of tf.Variable
  w.val.assign(newVals);
});

المحسن والخسارة والمقياس

قبل أن تقوم بأي تدريب، عليك أن تقرر ثلاثة أشياء:

  1. محسن . تتمثل مهمة المحسن في تحديد مقدار تغيير كل معلمة في النموذج، في ضوء التنبؤ الحالي للنموذج. عند استخدام واجهة برمجة التطبيقات للطبقات، يمكنك توفير إما معرف سلسلة لمحسن موجود (مثل 'sgd' أو 'adam' )، أو مثيل لفئة Optimizer .
  2. وظيفة الخسارة . هدف سيحاول النموذج تقليله. هدفه هو إعطاء رقم واحد يوضح "مدى خطأ" تنبؤ النموذج. يتم حساب الخسارة على كل دفعة من البيانات حتى يتمكن النموذج من تحديث أوزانه. عند استخدام واجهة برمجة التطبيقات للطبقات، يمكنك توفير إما معرف سلسلة لوظيفة خسارة موجودة (مثل 'categoricalCrossentropy' )، أو أي وظيفة تأخذ قيمة متوقعة وقيمة حقيقية وترجع الخسارة. راجع قائمة الخسائر المتاحة في مستندات API الخاصة بنا.
  3. قائمة المقاييس. على غرار الخسائر، تحسب المقاييس رقمًا واحدًا، مما يلخص مدى جودة أداء نموذجنا. عادةً ما يتم حساب المقاييس على البيانات الكاملة في نهاية كل حقبة. على أقل تقدير، نريد أن نراقب انخفاض خسارتنا بمرور الوقت. ومع ذلك، فإننا غالبًا ما نريد مقياسًا أكثر ملاءمةً للإنسان مثل الدقة. عند استخدام واجهة برمجة التطبيقات للطبقات، يمكنك توفير إما معرف سلسلة لمقياس موجود (مثل 'accuracy' )، أو أي وظيفة تأخذ قيمة متوقعة وحقيقية وترجع النتيجة. راجع قائمة المقاييس المتاحة في مستندات API الخاصة بنا.

عندما تقرر ذلك، قم بتجميع LayersModel عن طريق استدعاء model.compile() ‎ مع الخيارات المتوفرة:

model.compile({
  optimizer: 'sgd',
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy']
});

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

تمرين

هناك طريقتان لتدريب LayersModel :

  • استخدام model.fit() ‎ وتوفير البيانات كموتر واحد كبير.
  • استخدام model.fitDataset() وتوفير البيانات عبر كائن Dataset .

نموذج.صالح ()

إذا كانت مجموعة البيانات الخاصة بك مناسبة للذاكرة الرئيسية، ومتاحة كموتر واحد، فيمكنك تدريب نموذج عن طريق استدعاء الأسلوب fit() :

// Generate dummy data.
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);

function onBatchEnd(batch, logs) {
  console.log('Accuracy', logs.acc);
}

// Train for 5 epochs with batch size of 32.
model.fit(data, labels, {
   epochs: 5,
   batchSize: 32,
   callbacks: {onBatchEnd}
 }).then(info => {
   console.log('Final accuracy', info.history.acc);
 });

تحت الغطاء، يمكن model.fit() أن يفعل الكثير لنا:

  • يقسم البيانات إلى مجموعة تدريب وتحقق، ويستخدم مجموعة التحقق لقياس التقدم أثناء التدريب.
  • خلط البيانات ولكن فقط بعد التقسيم. لكي تكون آمنًا، يجب عليك خلط البيانات مسبقًا قبل تمريرها إلى fit() .
  • يقسم موتر البيانات الكبيرة إلى موترات أصغر بحجم batchSize.
  • يستدعي optimizer.minimize() أثناء حساب فقدان النموذج فيما يتعلق بمجموعة البيانات.
  • يمكنه إخطارك ببداية ونهاية كل حقبة أو دفعة. في حالتنا، يتم إعلامنا في نهاية كل دفعة باستخدام خيار callbacks.onBatchEnd . تتضمن الخيارات الأخرى: onTrainBegin و onTrainEnd و onEpochBegin و onEpochEnd و onBatchBegin .
  • إنه يخضع للخيط الرئيسي للتأكد من إمكانية معالجة المهام الموضوعة في قائمة الانتظار في حلقة أحداث JS في الوقت المناسب.

لمزيد من المعلومات، راجع وثائق fit() . لاحظ أنه إذا اخترت استخدام Core API، فسيتعين عليك تنفيذ هذا المنطق بنفسك.

نموذج.fitDataset()

إذا كانت بياناتك لا تتناسب تمامًا مع الذاكرة، أو يتم دفقها، فيمكنك تدريب نموذج عن طريق استدعاء fitDataset() ، الذي يأخذ كائن Dataset . إليك نفس كود التدريب ولكن مع مجموعة بيانات تحتوي على وظيفة المولد:

function* data() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomNormal([784]);
 }
}

function* labels() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomUniform([10]);
 }
}

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// We zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

// Train the model for 5 epochs.
model.fitDataset(ds, {epochs: 5}).then(info => {
 console.log('Accuracy', info.history.acc);
});

لمزيد من المعلومات حول مجموعات البيانات، راجع وثائق model.fitDataset() .

التنبؤ بالبيانات الجديدة

بمجرد تدريب النموذج، يمكنك استدعاء model.predict() ‎ لإجراء تنبؤات بشأن البيانات غير المرئية:

// Predict 3 random samples.
const prediction = model.predict(tf.randomNormal([3, 784]));
prediction.print();

واجهة برمجة التطبيقات الأساسية

ذكرنا سابقًا أن هناك طريقتين لتدريب نموذج التعلم الآلي في TensorFlow.js.

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

قد ترغب في استخدام Core API في أي وقت:

  • أنت بحاجة إلى أقصى قدر من المرونة أو التحكم.
  • ولا تحتاج إلى التسلسل، أو يمكنك تنفيذ منطق التسلسل الخاص بك.

لمزيد من المعلومات حول واجهة برمجة التطبيقات هذه، اقرأ قسم "واجهة برمجة التطبيقات الأساسية" في دليل النماذج والطبقات .

يبدو نفس النموذج المكتوب أعلاه باستخدام Core API كما يلي:

// The weights and biases for the two dense layers.
const w1 = tf.variable(tf.randomNormal([784, 32]));
const b1 = tf.variable(tf.randomNormal([32]));
const w2 = tf.variable(tf.randomNormal([32, 10]));
const b2 = tf.variable(tf.randomNormal([10]));

function model(x) {
  return x.matMul(w1).add(b1).relu().matMul(w2).add(b2);
}

بالإضافة إلى واجهة برمجة التطبيقات للطبقات، تعمل واجهة برمجة تطبيقات البيانات أيضًا بسلاسة مع واجهة برمجة التطبيقات الأساسية. دعونا نعيد استخدام مجموعة البيانات التي حددناها سابقًا في قسم model.fitDataset() ، والتي تقوم بالخلط والتجميع نيابةً عنا:

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// Zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

دعونا ندرب النموذج:

const optimizer = tf.train.sgd(0.1 /* learningRate */);
// Train for 5 epochs.
for (let epoch = 0; epoch < 5; epoch++) {
  await ds.forEachAsync(({xs, ys}) => {
    optimizer.minimize(() => {
      const predYs = model(xs);
      const loss = tf.losses.softmaxCrossEntropy(ys, predYs);
      loss.data().then(l => console.log('Loss', l));
      return loss;
    });
  });
  console.log('Epoch', epoch);
}

الكود أعلاه هو الوصفة القياسية عند تدريب نموذج باستخدام Core API:

  • حلقة على عدد العصور.
  • داخل كل عصر، قم بتكرار مجموعات البيانات الخاصة بك. عند استخدام Dataset ، تعتبر dataset.forEachAsync() طريقة مناسبة للتكرار على دفعاتك.
  • لكل دفعة، اتصل بـ optimizer.minimize(f) ، الذي ينفذ f ويقلل مخرجاته عن طريق حساب التدرجات فيما يتعلق بالمتغيرات الأربعة التي حددناها سابقًا.
  • f يحسب الخسارة. يستدعي إحدى وظائف الخسارة المحددة مسبقًا باستخدام التنبؤ بالنموذج والقيمة الحقيقية.