التعلم الآلي. الشبكات العصبية (الجزء الأول): عملية التعلم الخاصة بالمدرك
في هذه المقالة ، سوف نستخدم شبكة عصبية لنمذجة تنفيذ العمليات المنطقية OR ؛ XOR ، وهو نوع من تطبيقات "Hello World" للشبكات العصبية.
ستصف المقالة خطوة بخطوة عملية مثل هذه النمذجة باستخدام TensorFlow.js.
لذلك دعونا نبني شبكة عصبية لعملية OR المنطقية. عند الإدخال ، سنرسل دائمًا إشارتين X 1 و X 2 ، وعند المخرجات سوف نتلقى إشارة خرج واحدة Y. لتدريب الشبكة العصبية ، نحتاج أيضًا إلى مجموعة بيانات تدريب (الشكل 1).
الشكل 1 - مجموعة بيانات تدريبية ونموذج لنمذجة عملية OR منطقية
لفهم بنية الشبكة العصبية المراد تعيينها ، دعنا نتخيل مجموعة بيانات تدريب على مستوى إحداثيات باستخدام المحورين X 1 و X 2 (الشكل 2 ، يسار).
الشكل 2 - مجموعة التدريب على المستوى الإحداثي للتشغيل المنطقي أو
يرجى ملاحظة أنه لحل هذه المشكلة ، يكفي أن نرسم خطًا يقسم المستوى بحيث توجد جميع القيم TRUE على جانب واحد من الخط ، وعلى الجانب الآخر - جميع القيم FALSE (الشكل 2 ، صحيح). نعلم أيضًا أن خلية عصبية واحدة في الشبكة العصبية (perceptron) يمكنها التعامل تمامًا مع هذا الغرض ، حيث يتم حساب قيمة الإخراج من إشارات الإدخال على النحو التالي:
وهو تمثيل رياضي لمعادلة الخط المستقيم.
في ضوء حقيقة أن قيمنا في النطاق من 0 إلى 1 ، فإننا نطبق أيضًا وظيفة التنشيط السيني. وهكذا ، تبدو شبكتنا العصبية كما في الشكل 3.
الشكل 3 - الشبكة العصبية لتدريب العمليات المنطقية أو ،
لذلك دعونا نحل هذه المشكلة باستخدام TensorFlow.js.
أولاً ، نحتاج إلى تحويل مجموعة بيانات التدريب إلى موترات. الموتر هو حاوية بيانات يمكن أن تحتوي علىمحاور N وعدد عشوائي من العناصر على طول كل محور. معظم الموترات على دراية بالرياضيات - المتجهات (موتر بمحور واحد) ، المصفوفات (موتر بمحورين - صفوف ، أعمدة).
لتحديد مجموعة بيانات التدريب ، يكون المحور الأول (المحور 0) دائمًا هو المحور الذي توجد على طوله جميع مثيلات عينات البيانات المتاحة (الشكل 4).
الشكل 4 - هيكل الموتر
في حالتنا المحددة ، لدينا 4 حالات لعينات البيانات (الشكل 1) ، مما يعني أن موتر الإدخال على طول المحور الأول سيكون به 4 عناصر. كل عنصر من عناصر عينة التدريب عبارة عن ناقل يتكون من عنصرين X1، X2... وبالتالي ، فإن موتر الإدخال يحتوي على محورين (مصفوفة) ، على طول المحور الأول هناك 4 عناصر ، على طول المحور الثاني - عنصران.
const input = [[0, 0], [1, 0], [0, 1], [1, 1]];
const inputTensor = tf.tensor(input, [input.length, 2]);
وبالمثل ، قم بتحويل الإخراج إلى موتر. بالنسبة لإشارات الإدخال ، على طول المحور الأول لدينا 4 عناصر ، ويحتوي كل عنصر على متجه يحتوي على قيمة واحدة:
const output = [[0], [1], [1], [1]]
const outputTensor = tf.tensor(output, [output.length, 1]);
لنقم بإنشاء نموذج باستخدام TensorFlow API:
const model = tf.sequential();
model.add(
tf.layers.dense({ inputShape: [2], units: 1, activation: 'sigmoid' })
);
سيبدأ إنشاء النموذج دائمًا باستدعاء tf.sequential () . اللبنة الأساسية للنموذج هي الطبقات. يمكننا الاتصال بالنموذج بعدد طبقات الشبكة العصبية الذي نحتاجه. هنا نستخدم طبقة كثيفة ، مما يعني أن كل خلية عصبية في الطبقة التالية لها اتصال مع كل خلية عصبية في الطبقة السابقة. على سبيل المثال ، إذا كان لدينا طبقتان كثيفتان ، في الطبقة الأولى الخلايا العصبية ، وفي الثانية - ، سيكون العدد الإجمالي للوصلات بين الطبقات .
في حالتنا ، كما نرى ، تتكون الشبكة العصبية من طبقة واحدة ، يوجد فيها خلية عصبية واحدة ، وبالتالي يتم ضبط الوحدات على واحدة.
أيضًا ، بالنسبة للطبقة الأولى من الشبكة العصبية ، يجب علينا تعيين شكل الإدخال ، حيث يتم تمثيل كل مثيل إدخال بواسطة متجه من قيمتين X1و X2، وبالتالي شكل الإدخال = [2] . لاحظ أنهليست هناك حاجةلتعيين شكل الإدخال للطبقات المتوسطة - يمكن لـ TensorFlow تحديد هذه القيمة من قيمة الوحدات الخاصةبالطبقة السابقة.
أيضًا ، إذا لزم الأمر ، يمكن تعيين وظيفة تنشيط لكل طبقة ، وقد قررنا أعلاه أن هذه ستكون وظيفة سينية. يمكن العثور على وظائف التنشيط المتاحة حاليًا في TensorFlow هنا .
بعد ذلك ، نحتاج إلى تجميع النموذج (انظر API هنا ) ، بينما نحتاج إلى تعيين معلمتين مطلوبتين - هذه هي وظيفة الخطأ ونوع المحسن الذي سيبحث عن الحد الأدنى له:
model.compile({
optimizer: tf.train.sgd(0.1),
loss: 'meanSquaredError'
});
لقد قمنا بتعيين هبوط التدرج العشوائي على أنه المُحسِّن بخطوة تدريب 0.1.
قائمة المحسّنين المنفذين في المكتبة: tf.train.sgd ، tf.train.momentum ، tf.train.adagrad ، tf.train.adadelta ، tf.train.adam ، tf.train.adamax ، tf.train.rmsprop .
من الناحية العملية ، بشكل افتراضي ، يمكنك تحديد مُحسِّن adam على الفور ، والذي يحتوي على أفضل معدلات تقارب النماذج ، على عكس sgd - يتم تعيين معدل التعلم في كل مرحلة من مراحل التدريب اعتمادًا على تاريخ الخطوات السابقة وليس ثابتًا طوال عملية التعلم بأكملها.
كدالة خطأ ، يتم تقديمها من خلال دالة الخطأ التربيعي لمتوسط الجذر:
تم تعيين النموذج ، والخطوة التالية هي عملية تدريب النموذج ، لذلك يجب استدعاء طريقة الملاءمة على النموذج :
async function initModel() {
// skip for brevity
await model.fit(trainingInputTensor, trainingOutputTensor, {
epochs: 1000,
shuffle: true,
callbacks: {
onEpochEnd: async (epoch, { loss }) => {
// any actions on during any epoch of training
await tf.nextFrame();
}
}
})
}
لقد حددنا أن عملية التعلم يجب أن تتكون من 100 خطوة تعلم (عدد فترات التعلم) ؛ أيضًا في كل حقبة متتالية - يجب خلط بيانات الإدخال بترتيب عشوائي ( خلط عشوائي = صحيح ) - مما يؤدي إلى تسريع عملية تقارب النموذج ، حيث توجد حالات قليلة في مجموعة بيانات التدريب الخاصة بنا (4).
بعد الانتهاء من عملية التدريب ، يمكننا استخدام طريقة التنبؤ ، والتي ، بناءً على إشارات الإدخال الجديدة ، ستحسب قيمة الإخراج.
const testInput = generateInputs(10);
const testInputTensor = tf.tensor(testInput, [testInput.length, 2]);
const output = model.predict(testInputTensor).arraySync();
و generateInputs طريقة ببساطة يولد مجموعة بيانات 10X10 العينة التي تقسم تنسيق الطائرة إلى 100 الساحات:
يتم إعطاء الرمز الكامل هنا
import React, { useEffect, useState } from 'react';
import LossPlot from './components/LossPlot';
import Canvas from './components/Canvas';
import * as tf from "@tensorflow/tfjs";
let model;
export default () => {
const [data, changeData] = useState([]);
const [lossHistory, changeLossHistory] = useState([]);
useEffect(() => {
async function initModel() {
const input = [[0, 0], [1, 0], [0, 1], [1, 1]];
const inputTensor = tf.tensor(input, [input.length, 2]);
const output = [[0], [1], [1], [1]]
const outputTensor = tf.tensor(output, [output.length, 1]);
const testInput = generateInputs(10);
const testInputTensor = tf.tensor(testInput, [testInput.length, 2]);
model = tf.sequential();
model.add(
tf.layers.dense({ inputShape:[2], units:1, activation: 'sigmoid'})
);
model.compile({
optimizer: tf.train.adam(0.1),
loss: 'meanSquaredError'
});
await model.fit(inputTensor, outputTensor, {
epochs: 100,
shuffle: true,
callbacks: {
onEpochEnd: async (epoch, { loss }) => {
changeLossHistory((prevHistory) => [...prevHistory, {
epoch,
loss
}]);
const output = model.predict(testInputTensor)
.arraySync();
changeData(() => output.map(([out], i) => ({
out,
x1: testInput[i][0],
x2: testInput[i][1]
})));
await tf.nextFrame();
}
}
})
}
initModel();
}, []);
return (
<div>
<Canvas data={data} squareAmount={10}/>
<LossPlot loss={lossHistory}/>
</div>
);
}
function generateInputs(squareAmount) {
const step = 1 / squareAmount;
const input = [];
for (let i = 0; i < 1; i += step) {
for (let j = 0; j < 1; j += step) {
input.push([i, j]);
}
}
return input;
}
في الشكل التالي سترى جزءًا من عملية التعلم:

تنفيذ بلانكر:
محاكاة العملية المنطقية XOR
مجموعة التدريب لهذه الوظيفة موضحة في الشكل 6 ، وسنضع هذه النقاط أيضًا كما فعلنا للعملية المنطقية أو على مستوى الإحداثيات
الشكل 6 - مجموعة بيانات التدريب ونموذج لنمذجة العملية المنطقية حصرية أو (XOR)
يرجى ملاحظة ذلك على عكس العملية المنطقية OR ، لا يمكنك تقسيم المستوى بخط مستقيم واحد ، بحيث توجد في جانب واحد كل القيم TRUE ، وعلى الجانب الآخر - الكل FALSE . ومع ذلك ، يمكننا القيام بذلك باستخدام منحنيين (الشكل 7).
من الواضح ، في هذه الحالة ، أن خلية عصبية واحدة في الطبقة لا تكفي - فأنت بحاجة إلى طبقة أخرى على الأقل تحتوي على خليتين عصبيتين ، كل منهما سيحدد أحد الخطين على المستوى.
الشكل 7 - نموذج الشبكة العصبية للعملية المنطقية EXCLUSIVE OR (XOR)
في الكود السابق ، نحتاج إلى إجراء تغييرات في عدة أماكن ، أحدها هو مجموعة بيانات التدريب نفسها:
const input = [[0, 0], [1, 0], [0, 1], [1, 1]];
const inputTensor = tf.tensor(input, [input.length, 2]);
const output = [[0], [1], [1], [0]]
const outputTensor = tf.tensor(output, [output.length, 1]);
المكان الثاني هو الهيكل المتغير للنموذج ، حسب الشكل 7:
model = tf.sequential();
model.add(
tf.layers.dense({ inputShape: [2], units: 2, activation: 'sigmoid' })
);
model.add(
tf.layers.dense({ units: 1, activation: 'sigmoid' })
);
تبدو عملية التعلم في هذه الحالة كما يلي:
تنفيذ بلانكر:
موضوع المقال التالي
في المقالة التالية سوف نصف كيفية حل المشاكل المتعلقة بتصنيف الكائنات إلى فئات ، بناءً على قائمة ببعض الميزات.