صنع شبكة عصبية: كيف لا يكسر الدماغ

مرحبا يا هبر!

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

سيكون حول إنشاء شبكة عصبية تافهة على Keras ، والتي سوف نتوقع المتوسط ​​الحسابي للرقمين.

يبدو أنه يمكن أن يكون أسهل. وحقا ، لا شيء معقد ، ولكن هناك فروق دقيقة.

إلى من يهمه الموضوع ، مرحبًا بك ، لن يكون هناك أوصاف مملة طويلة ، فقط رمز قصير وتعليقات عليه.

الحل يبدو شيء مثل هذا:

import numpy as np from keras.layers import Input, Dense, Lambda from keras.models import Model import keras.backend as K #   def train_iterator(batch_size=64): x = np.zeros((batch_size, 2)) while True: for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 x_mean_ex = np.expand_dims(x_mean, -1) yield [x], [x_mean_ex] #  def create_model(): x = Input(name = 'x', shape=(2,)) x_mean = Dense(1)(x) model = Model(inputs=x, outputs=x_mean) return model #    model = create_model() model.compile(loss=['mse'], optimizer = 'rmsprop') model.fit_generator(train_iterator(), steps_per_epoch = 1000, epochs = 100, verbose = 1) #  x, x_mean = next(train_iterator(1)) print(x, x_mean, model.predict(x)) 

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

 Epoch 1/100 1000/1000 [==============================] - 2s 2ms/step - loss: 1044.0806 Epoch 2/100 1000/1000 [==============================] - 2s 2ms/step - loss: 713.5198 Epoch 3/100 1000/1000 [==============================] - 3s 3ms/step - loss: 708.1110 ... Epoch 98/100 1000/1000 [==============================] - 2s 2ms/step - loss: 415.0479 Epoch 99/100 1000/1000 [==============================] - 2s 2ms/step - loss: 416.6932 Epoch 100/100 1000/1000 [==============================] - 2s 2ms/step - loss: 417.2400 [array([[73., 57.]])] [array([[65.]])] [[49.650894]] 

تم توقع 49 ، وهو بعيد عن 65.

ولكن بمجرد إعادة المولد قليلاً ، يبدأ كل شيء بالعمل على الفور.

 def train_iterator_1(batch_size=64): x = np.zeros((batch_size, 2)) x_mean = np.zeros((batch_size,)) while True: for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean[::] = (x[::,0] + x[::,1]) / 2 x_mean_ex = np.expand_dims(x_mean, -1) yield [x], [x_mean_ex] 

ومن الواضح أن الشبكة تتقارب بالفعل في الحقبة الثالثة.

 Epoch 1/5 1000/1000 [==============================] - 2s 2ms/step - loss: 648.9184 Epoch 2/5 1000/1000 [==============================] - 2s 2ms/step - loss: 0.0177 Epoch 3/5 1000/1000 [==============================] - 2s 2ms/step - loss: 0.0030 

الاختلاف الرئيسي هو أنه في الحالة الأولى ، يتم إنشاء كائن x_mean في الذاكرة في كل مرة ، وفي الحالة الثانية ، يظهر عند إنشاء المولد ثم إعادة استخدامه فقط.

نحن نفهم كذلك ما إذا كان كل شيء صحيح في هذا المولد. اتضح أن ليس حقا.
يوضح المثال التالي أن هناك خطأ ما.
 def train_iterator(batch_size=1): x = np.zeros((batch_size, 2)) while True: for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield x, x_mean it = train_iterator() print(next(it), next(it)) 

(array([[44., 2.]]), array([10.])) (array([[44., 2.]]), array([23.]))

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

هناك طريقتان لإصلاح هذا. كلاهما مكلف ولكنه صحيح.
1. انقل إنشاء المتغير x داخل حلقة الاثناء ، بحيث يتم إنشاء مصفوفة جديدة عند كل محصول.
 def train_iterator_1(batch_size=1): while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield x, x_mean it_1 = train_iterator_1() print(next(it_1), next(it_1)) 

(array([[82., 4.]]), array([43.])) (array([[77., 34.]]), array([55.5]))


2. إرجاع نسخة من مجموعة.
 def train_iterator_2(batch_size=1): x = np.zeros((batch_size, 2)) while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield np.copy(x), x_mean it_2 = train_iterator_2() print(next(it_2), next(it_2)) 

(array([[63., 31.]]), array([47.])) (array([[94., 25.]]), array([59.5]))


الآن كل شيء على ما يرام. المضي قدما.

هل يجب أن يتم توسيعه؟ دعنا نحاول إزالة هذا السطر وسيكون الرمز الجديد كما يلي:

 def train_iterator(batch_size=64): while True: x = np.zeros((batch_size, 2)) for i in range(batch_size): x[i][0] = np.random.randint(0, 100) x[i][1] = np.random.randint(0, 100) x_mean = (x[::,0] + x[::,1]) / 2 yield [x], [x_mean] 

كل شيء يتعلم بشكل جيد ، على الرغم من أن البيانات التي تم إرجاعها لها شكل مختلف.

على سبيل المثال ، كان هناك [[49.]] ، وأصبح [49.] ، ولكن داخل Keras ، على ما يبدو ، تم تقليل هذا بشكل صحيح إلى البعد المطلوب.

لذلك ، نحن نعرف الشكل الذي يجب أن يبدو عليه مولد البيانات الصحيح ، فلنلعب الآن مع وظيفة lambda ، وننظر إلى سلوك expand_dims هناك.

لن نتوقع أي شيء ، نحن فقط نعتبر القيمة الصحيحة داخل لامدا.

الكود كما يلي:

 def calc_mean(x): res = (x[::,0] + x[::,1]) / 2 res = K.expand_dims(res, -1) return res def create_model(): x = Input(name = 'x', shape=(2,)) x_mean = Lambda(lambda x: calc_mean(x), output_shape=(1,))(x) model = Model(inputs=x, outputs=x_mean) return model 

نبدأ ونرى أن كل شيء على ما يرام:

 Epoch 1/5 100/100 [==============================] - 0s 3ms/step - loss: 0.0000e+00 Epoch 2/5 100/100 [==============================] - 0s 2ms/step - loss: 0.0000e+00 Epoch 3/5 100/100 [==============================] - 0s 3ms/step - loss: 0.0000e+00 

الآن ، دعونا نحاول تعديل وظيفة lambda الخاصة بنا وإزالة expand_dims.

 def calc_mean(x): res = (x[::,0] + x[::,1]) / 2 return res 

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

 Epoch 1/5 100/100 [==============================] - 0s 3ms/step - loss: 871.6299 Epoch 2/5 100/100 [==============================] - 0s 3ms/step - loss: 830.2568 Epoch 3/5 100/100 [==============================] - 0s 2ms/step - loss: 830.8041 

وإذا نظرت إلى النتيجة المرتجعة من التنبؤ () ، يمكنك أن ترى أن البعد خطأ ، والإخراج هو [46.] ، والمتوقع [[46.]].

شيء من هذا القبيل. شكرا لكل من قرأها. وكن حذرًا في التفاصيل ، فقد يكون تأثيرها كبيرًا.

Source: https://habr.com/ru/post/ar439038/


All Articles