من حل المعادلات إلى التعلم العميق: برنامج تعليمي TensorFlow Python

نشرت: 2022-03-11

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

من بين هذه التطورات عدد من الأدوات للمساعدة في اشتقاق التعلم العميق ونماذج التعلم الآلي الأخرى ، مع Torch و Caffe و Theano من بين تلك الموجودة في المقدمة. ومع ذلك ، منذ أن أصبح Google Brain مفتوح المصدر في نوفمبر 2015 بإطار عمل خاص به ، TensorFlow ، رأينا ارتفاع شعبية مكتبة البرامج هذه لتكون أكثر إطار عمل التعلم العميق شيوعًا.

TensorFlow

لماذا حدث هذا؟ تشمل الأسباب وفرة الدعم والتوثيق المتاح ، واستعداده للإنتاج ، وسهولة توزيع الحسابات عبر مجموعة من الأجهزة ، وأداة تصور ممتازة: TensorBoard.

في النهاية ، تمكنت TensorFlow من الجمع بين مجموعة شاملة ومرنة من الميزات التقنية مع سهولة كبيرة في الاستخدام.

في هذه المقالة ، سوف تكتسب فهمًا لآليات هذه الأداة من خلال استخدامها لحل مشكلة عددية عامة ، بعيدًا تمامًا عما يتضمنه التعلم الآلي عادةً ، قبل تقديم استخداماتها في التعلم العميق من خلال تنفيذ بسيط للشبكة العصبية.

قبل ان تبدأ

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

نظرًا لأننا سنعرض Python API ، فإن فهم Numpy مفيد أيضًا.

لإعداد TensorFlow ، يرجى اتباع التعليمات الموجودة هنا.

إذا كنت تستخدم Windows ، فيجب ملاحظة أنه في وقت كتابة هذا التقرير ، يجب عليك استخدام Python 3.4+ وليس 2.7.

ثم عندما تكون جاهزًا ، يجب أن تكون قادرًا على استيراد المكتبة باستخدام:

 import tensorflow as tf

الخطوة 1 من 2 لحل TensorFlow: قم بإنشاء رسم بياني

يتكون بناء برامج TensorFlow بشكل عام من خطوتين رئيسيتين ، أولهما هو بناء رسم بياني حسابي ، والذي سيصف الحسابات التي ترغب في تنفيذها ، ولكن لا يقوم بها في الواقع أو يحمل أي قيم.

كما هو الحال مع أي رسم بياني ، لدينا عقد وحواف. تمثل الحواف موترًا ، موترًا يمثل مصفوفة ذات أبعاد n. على سبيل المثال ، الموتر ذو البعد (أو الرتبة في حديث TensorFlow) 0 هو عدد قياسي ، والرتبة 1 متجه ، والرتبة 2 مصفوفة وما إلى ذلك.

تمثل العقد العمليات التي تنتج موترًا للإخراج ، مع أخذ الموترات كمدخلات إذا لزم الأمر. تتضمن هذه العمليات عمليات الجمع ( tf.add ) ، ومضاعفات المصفوفة ( tf.matmul ) ، وكذلك إنشاء الثوابت ( tf.constant ).

لذا ، دعنا نجمع القليل منها في التمثيل البياني الأول.

 a = tf.constant([2.5, 2]) b = tf.constant([3, 6], dtype=tf.float32) total = tf.add(a, b)

لقد أنشأنا هنا ثلاث عمليات ، اثنتان منها لإنشاء مصفوفات ثابتة أحادية البعد.

يتم الاستدلال على أنواع البيانات من وسيطة القيم التي تم تمريرها ، أو يمكنك الإشارة إليها باستخدام وسيطة dtype . إذا لم أفعل هذا من أجل b ، فسيتم استنتاج أن int32 وخطأ يُلقى على النحو tf.add كان سيحاول تحديد إضافة على نوعين مختلفين.

الخطوة 2 من 2 إلى حل TensorFlow: نفِّذ العمليات

تم تعريف الرسم البياني ، ولكن من أجل إجراء أي حسابات عليه (أو أي جزء منه) فعلينا إعداد جلسة TensorFlow.

 sess = tf.Session()

بدلاً من ذلك ، إذا كنا نجري جلسة في غلاف تفاعلي ، مثل IPython ، فإننا نستخدم:

 sess = tf.InteractiveSession()

طريقة run على كائن الجلسة هي إحدى طرق تقييم Tensor.

لذلك ، لتقييم حساب الإضافة المحدد أعلاه ، نقوم بتمرير "total" ، Tensor للاسترداد ، والتي تمثل ناتج tf.add op.

 print(sess.run(total)) # [ 5.5 8. ]

في هذه المرحلة ، نقدم فئة TensorFlow's Variable. في حين أن الثوابت هي جزء ثابت من تعريف الرسم البياني ، يمكن تحديث المتغيرات. يتطلب مُنشئ الصنف قيمة أولية ، ولكن حتى ذلك الحين ، تحتاج المتغيرات إلى عملية لتهيئتها بشكل صريح قبل تنفيذ أي عمليات أخرى عليها.

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

 # Create a variable with an initial value of 1 some_var = tf.Variable(1) # Create op to run variable initializers init_op = tf.global_variables_initializer() # Create an op to replace the value held by some_var to 3 assign_op = some_var.assign(3) # Set up two instances of a session sess1 = tf.Session() sess2 = tf.Session() # Initialize variables in both sessions sess1.run(init_op) sess2.run(init_op) print(sess1.run(some_var)) # Outputs 1 # Change some_var in session1 sess1.run(assign_op) print(sess1.run(some_var)) # Outputs 3 print(sess2.run(some_var)) # Outputs 1 # Close sessions sess1.close() sess2.close()

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

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

تغذية الرسم البياني لمعالجة المشاكل العددية

مفهوم آخر مهم لـ TensorFlow هو العنصر النائب. في حين أن المتغيرات تحتفظ بالحالة ، يتم استخدام العناصر النائبة لتحديد المدخلات التي يمكن أن يتوقعها الرسم البياني ونوع بياناتها (واختيارياً شكلها). بعد ذلك يمكننا إدخال البيانات في الرسم البياني عبر هذه العناصر النائبة عند تشغيل الحساب.

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

لنفترض أننا نريد إيجاد y في معادلة مثل هذه:

ت = Ce -0.5y + Ce -y + Ce -1.5y + (C + P) e -2y

لـ v (مع ثابت C و P ).

هذه صيغة لحساب العائد حتى الاستحقاق ( y ) على سند بقيمة سوقية v ، وأصل P ، وكوبون C مدفوع نصف سنوي ولكن مع خصم التدفقات النقدية مع المركب المستمر.

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

أولاً ، سنضع هذه المشكلة على شكل رسم بياني TensorFlow.

C و P ثوابت ثابتة وتشكلان جزءًا من تعريف الرسم البياني الخاص بنا. نرغب في الحصول على عملية تنقية الحد الأدنى والأعلى لـ y . لذلك ، تعد هذه الحدود (المشار إليها بـ a و b ) مرشحة جيدة للمتغيرات التي يجب تغييرها بعد كل تخمين لـ y (تعتبر نقطة المنتصف a و b ).

 # Specify the values our constant ops will output C = tf.constant(5.0) P = tf.constant(100.0) # We specify the initial values that our lower and upper bounds will be when initialised. # Obviously the ultimate success of this algorithm depends on decent start points a = tf.Variable(-10.0) b = tf.Variable(10.0) # We expect a floating number to be inserted into the graph v_target = tf.placeholder("float") # Remember the following operations are definitions, # none are carried out until an operation is evaluated in a session! y = (a+b)/2 v_guess = C*tf.exp(-0.5*y) + C*tf.exp(-y) + C*tf.exp(-1.5*y) + (C + P)*tf.exp(-2*y) # Operations to set temporary values (a_ and b_) intended to be the next values of a and b. # eg if the guess results in av greater than the target v, # we will set a_ to be the current value of y a_ = tf.where(v_guess > v_target, y, a) b_ = tf.where(v_guess < v_target, y, b) # The last stage of our graph is to assign the two temporary vals to our variables step = tf.group( a.assign(a_), b.assign(b_) )

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

تعتمد بعض هذه العمليات على عنصر نائب يجب تحديد قيمة له ، فكيف يمكننا فعلاً تغذية هذه القيمة؟

يتم ذلك من خلال الوسيطة feed_dict في دالة run نفسها.

إذا أردنا تقييم a_ ، فإننا نعوض بقيمة العنصر النائب v_target ، مثل:

 sess.run(a_, feed_dict={v_target: 100})

يعطينا 0.0.

قم بتوصيل v_target من 130 ونحصل على -10.0.

إنها عمليتنا "التدريجية" التي تتطلب في الواقع تنفيذ جميع العمليات الأخرى كشرط مسبق وفي الواقع يتم تنفيذ الرسم البياني بأكمله. إنها أيضًا عملية تغير الحالة الفعلية عبر جلستنا. لذلك ، كلما نفذنا الخطوة ، زاد دفعنا المتغيرين a و b نحو القيمة الفعلية لـ y .

لنفترض أن قيمة v في معادلتنا تساوي 95. فلنقم بإعداد جلسة وتنفيذ الرسم البياني عليها 100 مرة.

 # Set up a session and initialize variables sess = tf.Session() tf.global_variables_initializer().run() # Run the step operation (and therefore whole graph) 100 times for i in range (100): sess.run(step, feed_dict={v_target:95})

إذا قيمنا موتر y الآن ، فسنحصل على شيء يشبه الإجابة المرغوبة

 print(sess.run(y)) # 0.125163

الشبكات العصبية

الآن بعد أن أصبح لدينا فهم لآليات TensorFlow ، يمكننا جمع هذا مع بعض عمليات التعلم الآلي الإضافية المضمنة في TensorFlow لتدريب شبكة عصبية بسيطة.

هنا ، نود تصنيف نقاط البيانات على نظام إحداثيات ثنائي الأبعاد اعتمادًا على ما إذا كانت تقع داخل منطقة معينة - دائرة نصف قطرها 0.5 متمركزة في الأصل.

بالطبع ، يمكن التحقق من ذلك بشكل ملموس من خلال التحقق فقط من نقطة معينة (a,b) إذا كان a^2 + b^2 < 0.5 ، ولكن لأغراض تجربة التعلم الآلي هذه ، نود بدلاً من ذلك تمرير مجموعة التدريب: سلسلة من النقاط العشوائية وما إذا كانت تقع في منطقتنا المقصودة. إليك طريقة واحدة لإنشاء هذا:

 import numpy as np NO_OF_RANDOM_POINTS = 100 CIRCLE_RADIUS = 0.5 random_spots = np.random.rand(NO_OF_RANDOM_POINTS, 2) * 2 - 1 is_inside_circle = (np.power(random_spots[:,0],2) + np.power(random_spots[:,1],2) < CIRCLE_RADIUS).astype(int)

سننشئ شبكة عصبية بالخصائص التالية:

  1. يتكون من طبقة إدخال ذات عقدتين ، حيث نقوم بتغذية سلسلة المتجهات ثنائية الأبعاد الموجودة في "النقاط العشوائية". سيتم تمثيل ذلك من خلال عنصر نائب ينتظر بيانات التدريب.
  2. ستحتوي طبقة المخرجات أيضًا على عقدتين ، لذلك نحتاج إلى تغذية سلسلة ملصقات التدريب الخاصة بنا ("is_inside_circle") في عنصر نائب لقياسي ، ثم تحويل هذه القيم إلى متجه ثنائي الأبعاد.
  3. سيكون لدينا طبقة واحدة مخفية تتكون من ثلاث عقد ، لذلك سنحتاج إلى استخدام متغيرات لمصفوفات الأوزان ومتجهات التحيز ، حيث أن هذه هي القيم التي يجب تحسينها عند إجراء التدريب.
 INPUT_LAYER_SIZE = 2 HIDDEN_LAYER_SIZE = 3 OUTPUT_LAYER_SIZE = 2 # Starting values for weights and biases are drawn randomly and uniformly from [-1, 1] # For example W1 is a matrix of shape 2x3 W1 = tf.Variable(tf.random_uniform([INPUT_LAYER_SIZE, HIDDEN_LAYER_SIZE], -1, 1)) b1 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE], -1, 1)) W2 = tf.Variable(tf.random_uniform([HIDDEN_LAYER_SIZE, OUTPUT_LAYER_SIZE], -1, 1)) b2 = tf.Variable(tf.random_uniform([OUTPUT_LAYER_SIZE], -1, 1)) # Specifying that the placeholder X can expect a matrix of 2 columns (but any number of rows) # representing random spots X = tf.placeholder(tf.float32, [None, INPUT_LAYER_SIZE]) # Placeholder Y can expect integers representing whether corresponding point is in the circle # or not (no shape specified) Y = tf.placeholder(tf.uint8) # An op to convert to a one hot vector onehot_output = tf.one_hot(Y, OUTPUT_LAYER_SIZE)

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

 LEARNING_RATE = 0.01 # Op to perform matrix calculation X*W1 + b1 hidden_layer = tf.add(tf.matmul(X, W1), b1) # Use sigmoid activation function on the outcome activated_hidden_layer = tf.sigmoid(hidden_layer) # Apply next weights and bias (W2, b2) to hidden layer and then apply softmax function # to get our output layer (each vector adding up to 1) output_layer = tf.nn.softmax(tf.add(tf.matmul(activated_hidden_layer, W2), b2)) # Calculate cross entropy for our loss function loss = -tf.reduce_sum(onehot_output * tf.log(output_layer)) # Use gradient descent optimizer at specified learning rate to minimize value given by loss tensor train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(loss)

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

 EPOCH_COUNT = 1000 sess = tf.Session() tf.global_variables_initializer().run() for i in range(EPOCH_COUNT): if i%100 == 0: print('Loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle}))) sess.run(train_step, feed_dict={X: random_spots, Y: is_inside_circle}) print('Final loss after %d runs: %f' % (i, sess.run(loss, feed_dict={X: random_spots, Y: is_inside_circle})))

بمجرد تدريب الخوارزمية ، يمكننا تغذية نقطة والحصول على ناتج الشبكة العصبية على النحو التالي:

 sess.run(output_layer, feed_dict={X: [[1, 1]]}) # Hopefully something close to [1, 0] sess.run(output_layer, feed_dict={X: [[0, 0]]}) # Hopefully something close to [0, 1]

يمكننا تصنيف النقطة على أنها خارج الدائرة إذا كان العضو الأول في متجه الإخراج أكبر من 0.5 ، وإلا في الداخل.

من خلال تشغيل موتر output_layer للعديد من النقاط ، يمكننا الحصول على فكرة عن كيفية تصور المتعلم للمنطقة التي تحتوي على النقاط المصنفة بشكل إيجابي. من الجدير التلاعب بحجم مجموعة التدريب ومعدل التعلم والمعلمات الأخرى لمعرفة مدى قربنا من الدائرة التي أردناها.

تقريبا مثلث
مجموعة التدريب: 100 نقطة
معدل التعلم: 0.01
العهود: 1000

مثلث صغير
مجموعة التدريب: 1000 نقطة
معدل التعلم: 0.01
العهود: 1000

مثلث كبير
مجموعة التدريب: 1000 نقطة
معدل التعلم: 0.01
العهود: 10000

تقريبا دائرة
مجموعة التدريب: 1000 نقطة
معدل التعلم: 0.001
العهود: 10000

يتم إحتوائه

هذا درس جيد وهو أن زيادة مجموعة التدريب أو مقدار الحقبة لا يضمن للمتعلم الجيد - يجب تعديل معدل التعلم بشكل مناسب.

نأمل أن تكون هذه العروض التوضيحية قد أعطتك نظرة ثاقبة للمبادئ الأساسية لـ TensorFlow ، وتوفر أساسًا متينًا لتنفيذ تقنيات أكثر تعقيدًا.

لم نقم بتغطية مفاهيم مثل Tensorboard أو تدريب نماذجنا عبر وحدات معالجة الرسومات ، ولكن تمت تغطية هذه المفاهيم جيدًا في وثائق TensorFlow. يمكن العثور على عدد من الوصفات في الوثائق التي يمكن أن تساعدك على التعجيل في معالجة مهام التعلم العميق المثيرة باستخدام هذا الإطار القوي!

الموضوعات ذات الصلة: التطبيقات العديدة للنسب المتدرج في TensorFlow