بساطة وتعقد البدائية أو كيفية تحديد المعالجة المسبقة غير الضرورية لشبكة عصبية

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



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

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

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

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







لن نكتشف مثلثًا ورباعي الزوايا في صورة واحدة ، وسنكتشفها بشكل منفصل ، في قطارات مختلفة ، على خلفية التداخل في شكل القطع الناقص.

دعنا نأخذ شبكة U-net الكلاسيكية وثلاثة أنواع من التسلسلات التدريبية مع المثلثات والرباعي والنجوم للبحث.

لذلك ، أعطيت:

  • ثلاث سلاسل تدريب لأزواج الصور / القناع ؛
  • الشبكة. شبكة U العادية ، والتي تستخدم على نطاق واسع للتجزئة.

فكرة الاختبار:

  • تحديد أي من تسلسل التدريب "أصعب" للتعلم ؛
  • كيف تؤثر بعض تقنيات المعالجة المسبقة على التعلم؟

دعنا نبدأ ، حدد 10000 زوج من صور الأرباع مع علامات الإهليلج والأقنعة والنظر فيها بعناية. نحن مهتمون بمدى اختصار سرير الأطفال وما هو طوله.

نقوم بتحميل المكتبات ، ونحدد أحجام مجموعة من الصور
import numpy as np import matplotlib.pyplot as plt %matplotlib inline import math from tqdm import tqdm from skimage.draw import ellipse, polygon from keras import Model from keras.optimizers import Adam from keras.layers import Input,Conv2D,Conv2DTranspose,MaxPooling2D,concatenate from keras.layers import BatchNormalization,Activation,Add,Dropout from keras.losses import binary_crossentropy from keras import backend as K import tensorflow as tf import keras as keras w_size = 128 train_num = 10000 radius_min = 10 radius_max = 20 


تحديد وظائف الخسارة والدقة
 def dice_coef(y_true, y_pred): y_true_f = K.flatten(y_true) y_pred = K.cast(y_pred, 'float32') y_pred_f = K.cast(K.greater(K.flatten(y_pred), 0.5), 'float32') intersection = y_true_f * y_pred_f score = 2. * K.sum(intersection) / (K.sum(y_true_f) + K.sum(y_pred_f)) return score def dice_loss(y_true, y_pred): smooth = 1. y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = y_true_f * y_pred_f score = (2. * K.sum(intersection) + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth) return 1. - score def bce_dice_loss(y_true, y_pred): return binary_crossentropy(y_true, y_pred) + dice_loss(y_true, y_pred) def get_iou_vector(A, B): # Numpy version batch_size = A.shape[0] metric = 0.0 for batch in range(batch_size): t, p = A[batch], B[batch] true = np.sum(t) pred = np.sum(p) # deal with empty mask first if true == 0: metric += (pred == 0) continue # non empty mask case. Union is never empty # hence it is safe to divide by its number of pixels intersection = np.sum(t * p) union = true + pred - intersection iou = intersection / union # iou metrric is a stepwise approximation of the real iou over 0.5 iou = np.floor(max(0, (iou - 0.45)*20)) / 10 metric += iou # teake the average over all images in batch metric /= batch_size return metric def my_iou_metric(label, pred): # Tensorflow version return tf.py_func(get_iou_vector, [label, pred > 0.5], tf.float64) from keras.utils.generic_utils import get_custom_objects get_custom_objects().update({'bce_dice_loss': bce_dice_loss }) get_custom_objects().update({'dice_loss': dice_loss }) get_custom_objects().update({'dice_coef': dice_coef }) get_custom_objects().update({'my_iou_metric': my_iou_metric }) 


سوف نستخدم المقياس من المقال الأول . دعني أذكّر القراء بأننا سنتنبأ بقناع البيكسل - هذه هي "الخلفية" أو "الرباعي" وتقييم حقيقة أو زيف التنبؤ. أي الخيارات الأربعة التالية ممكنة - لقد توقعنا بشكل صحيح أن البيكسل هي خلفية ، أو تنبأ بشكل صحيح أن البيكسل هو رباعي الزوايا ، أو ارتكبنا خطأً في التنبؤ بـ "خلفية" أو "رباعي الزوايا". وهكذا ، بالنسبة لجميع الصور وجميع وحدات البكسل ، نقدر عدد الخيارات الأربعة ونحسب النتيجة - سيكون ذلك نتيجة الشبكة. وكلما كانت التنبؤات أقل خطأ والأكثر صحة ، كانت النتيجة أكثر دقة وأفضل شبكة.

نحن نفحص الشبكة على أنها "الصندوق الأسود" ، ولن نبدأ في النظر في ما يحدث للشبكة في الداخل ، وكيف تتغير الأوزان وكيف يتم اختيار التدرجات - سننظر في أحشاء الشبكة في وقت لاحق عندما نقارن الشبكات.

شبكة بسيطة
 def build_model(input_layer, start_neurons): # 128 -> 64 conv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(input_layer) conv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(conv1) pool1 = MaxPooling2D((2, 2))(conv1) pool1 = Dropout(0.25)(pool1) # 64 -> 32 conv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(pool1) conv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(conv2) pool2 = MaxPooling2D((2, 2))(conv2) pool2 = Dropout(0.5)(pool2) # 32 -> 16 conv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(pool2) conv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(conv3) pool3 = MaxPooling2D((2, 2))(conv3) pool3 = Dropout(0.5)(pool3) # 16 -> 8 conv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(pool3) conv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(conv4) pool4 = MaxPooling2D((2, 2))(conv4) pool4 = Dropout(0.5)(pool4) # Middle convm = Conv2D(start_neurons * 16, (3, 3), activation="relu", padding="same")(pool4) convm = Conv2D(start_neurons * 16, (3, 3), activation="relu", padding="same")(convm) # 8 -> 16 deconv4 = Conv2DTranspose(start_neurons * 8, (3, 3), strides=(2, 2), padding="same")(convm) uconv4 = concatenate([deconv4, conv4]) uconv4 = Dropout(0.5)(uconv4) uconv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(uconv4) uconv4 = Conv2D(start_neurons * 8, (3, 3), activation="relu", padding="same")(uconv4) # 16 -> 32 deconv3 = Conv2DTranspose(start_neurons * 4, (3, 3), strides=(2, 2), padding="same")(uconv4) uconv3 = concatenate([deconv3, conv3]) uconv3 = Dropout(0.5)(uconv3) uconv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(uconv3) uconv3 = Conv2D(start_neurons * 4, (3, 3), activation="relu", padding="same")(uconv3) # 32 -> 64 deconv2 = Conv2DTranspose(start_neurons * 2, (3, 3), strides=(2, 2), padding="same")(uconv3) uconv2 = concatenate([deconv2, conv2]) uconv2 = Dropout(0.5)(uconv2) uconv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(uconv2) uconv2 = Conv2D(start_neurons * 2, (3, 3), activation="relu", padding="same")(uconv2) # 64 -> 128 deconv1 = Conv2DTranspose(start_neurons * 1, (3, 3), strides=(2, 2), padding="same")(uconv2) uconv1 = concatenate([deconv1, conv1]) uconv1 = Dropout(0.5)(uconv1) uconv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(uconv1) uconv1 = Conv2D(start_neurons * 1, (3, 3), activation="relu", padding="same")(uconv1) uncov1 = Dropout(0.5)(uconv1) output_layer = Conv2D(1, (1,1), padding="same", activation="sigmoid")(uconv1) return output_layer # model input_layer = Input((w_size, w_size, 1)) output_layer = build_model(input_layer, 26) model = Model(input_layer, output_layer) model.compile(loss=bce_dice_loss, optimizer=Adam(lr=1e-4), metrics=[my_iou_metric]) model.summary() 


وظيفة توليد أزواج الصور / القناع. على صورة بالأبيض والأسود 128 × 128 مليئة بالضوضاء العشوائية مع اختيار عشوائي من نطاقين ، أو 0.0 ... 0.75 أو 0.25..1.0. حدد بشكل عشوائي ربعًا في الصورة ، ثم ضع علامة ناقلة ذات اتجاه عشوائي ، وفي الربع الآخر نضع رباعي الزوايا ولونًا متساويًا مع ضوضاء عشوائية.

 def next_pair(): img_l = (np.random.sample((w_size, w_size, 1))* 0.75).astype('float32') img_h = (np.random.sample((w_size, w_size, 1))* 0.75 + 0.25).astype('float32') img = np.zeros((w_size, w_size, 2), dtype='float') i0_qua = math.trunc(np.random.sample()*4.) i1_qua = math.trunc(np.random.sample()*4.) while i0_qua == i1_qua: i1_qua = math.trunc(np.random.sample()*4.) _qua = np.int(w_size/4) qua = np.array([[_qua,_qua],[_qua,_qua*3],[_qua*3,_qua*3],[_qua*3,_qua]]) p = np.random.sample() - 0.5 r = qua[i0_qua,0] c = qua[i0_qua,1] r_radius = np.random.sample()*(radius_max-radius_min) + radius_min c_radius = np.random.sample()*(radius_max-radius_min) + radius_min rot = np.random.sample()*360 rr, cc = ellipse( r, c, r_radius, c_radius, rotation=np.deg2rad(rot), shape=img_l.shape ) p0 = np.rint(np.random.sample()*(radius_max-radius_min) + radius_min) p1 = qua[i1_qua,0] - (radius_max-radius_min) p2 = qua[i1_qua,1] - (radius_max-radius_min) p3 = np.rint(np.random.sample()*radius_min) p4 = np.rint(np.random.sample()*radius_min) p5 = np.rint(np.random.sample()*radius_min) p6 = np.rint(np.random.sample()*radius_min) p7 = np.rint(np.random.sample()*radius_min) p8 = np.rint(np.random.sample()*radius_min) poly = np.array(( (p1, p2), (p1+p3, p2+p4+p0), (p1+p5+p0, p2+p6+p0), (p1+p7+p0, p2+p8), (p1, p2), )) rr_p, cc_p = polygon(poly[:, 0], poly[:, 1], img_l.shape) if p > 0: img[:,:,:1] = img_l.copy() img[rr, cc,:1] = img_h[rr, cc] img[rr_p, cc_p,:1] = img_h[rr_p, cc_p] else: img[:,:,:1] = img_h.copy() img[rr, cc,:1] = img_l[rr, cc] img[rr_p, cc_p,:1] = img_l[rr_p, cc_p] img[:,:,1] = 0. img[rr_p, cc_p,1] = 1. return img 

لنقم بإنشاء تسلسل تدريبي للأزواج ، راجع عشوائي 10. دعني أذكرك بأن الصور أحادية اللون ، تدرج الرمادي.

 _txy = [next_pair() for idx in range(train_num)] f_imgs = np.array(_txy)[:,:,:,:1].reshape(-1,w_size ,w_size ,1) f_msks = np.array(_txy)[:,:,:,1:].reshape(-1,w_size ,w_size ,1) del(_txy) #    10   fig, axes = plt.subplots(2, 10, figsize=(20, 5)) for k in range(10): kk = np.random.randint(train_num) axes[0,k].set_axis_off() axes[0,k].imshow(f_imgs[kk]) axes[1,k].set_axis_off() axes[1,k].imshow(f_msks[kk].squeeze()) 



الخطوة الأولى نحن ندرب على الحد الأدنى مجموعة البداية


الخطوة الأولى من تجربتنا بسيطة ، فنحن نحاول تدريب الشبكة على التنبؤ بـ 11 صورة فقط.

 batch_size = 10 val_len = 11 precision = 0.85 m0_select = np.zeros((f_imgs.shape[0]), dtype='int') for k in range(val_len): m0_select[k] = 1 t = tqdm() while True: fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] t.set_description("accuracy {0:6.4f} loss {1:6.4f} ".\ format(current_accu, current_loss)) t.update(1) if current_accu > precision: break t.close() 

accuracy 0.8545 loss 0.0674 lenght 11 : : 793it [00:58, 14.79it/s]

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

الآن ابدأ التجربة الرئيسية


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

 batch_size = 50 t_batch_size = 1024 raw_len = val_len t = tqdm(-1) id_train = 0 #id_select = 1 while True: t.set_description("Accuracy {0:6.4f} loss {1:6.4f}\ selected img {2:5d} tested img {3:5d} ". format(current_accu, current_loss, val_len, raw_len)) t.update(1) if id_train == 1: fit = model.fit(f_imgs[m0_select>0], f_msks[m0_select>0], batch_size=batch_size, epochs=1, verbose=0 ) current_accu = fit.history['my_iou_metric'][0] current_loss = fit.history['loss'][0] if current_accu > precision: id_train = 0 else: t_pred = model.predict( f_imgs[raw_len: min(raw_len+t_batch_size,f_imgs.shape[0])], batch_size=batch_size ) for kk in range(t_pred.shape[0]): val_iou = get_iou_vector( f_msks[raw_len+kk].reshape(1,w_size,w_size,1), t_pred[kk].reshape(1,w_size,w_size,1) > 0.5) if val_iou < precision*0.95: new_img_test = 1 m0_select[raw_len+kk] = 1 val_len += 1 break raw_len += (kk+1) id_train = 1 if raw_len >= train_num: break t.close() 

 Accuracy 0.9338 loss 0.0266 selected img 1007 tested img 9985 : : 4291it [49:52, 1.73s/it] 

يتم استخدام الدقة هنا بمعنى "الدقة" ، وليس كمقياس keras قياسي ، ويتم استخدام الروتين الفرعي "my_iou_metric" لحساب الدقة.

الآن قم بمقارنة تشغيل نفس الشبكة مع نفس المعلمات على تسلسل مختلف ، على المثلثات



ونحصل على نتيجة مختلفة تماما

 Accuracy 0.9823 loss 0.0108 selected img 1913 tested img 9995 : : 6343it [2:11:36, 3.03s/it] 

حددت الشبكة 1913 صورة تحتوي على معلومات "جديدة" ، أي محتوى الصور مع المثلثات هو نصف محتوى الصور الرباعية!

دعونا نتحقق من نفس الشيء على النجوم ونشغل الشبكة في التسلسل الثالث



نحن نحصل عليها

 Accuracy 0.8985 loss 0.0478 selected img 476 tested img 9985 : : 2188it [16:13, 1.16it/s] 

كما ترون ، تحولت النجوم إلى 476 صورة فقط في ورقة الغش.

كان لدينا سبب للحكم على تعقيد الأشكال الهندسية للإدراك من قبل شبكتهم العصبية. أبسط هو النجم ، مع 476 صورة فقط في ورقة الغش ، ثم تبين أن رباعي الزوايا مع 1007 والأكثر تعقيدا مثلث - للتدريب تحتاج إلى 1913 صورة.

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

الآن عن الجاد


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

 from scipy.ndimage import gaussian_filter _tmp = [gaussian_filter(idx, sigma = 1) for idx in f_imgs] f1_imgs = np.array(_tmp)[:,:,:,:1].reshape(-1,w_size ,w_size ,1) del(_tmp) fig, axes = plt.subplots(2, 5, figsize=(20, 7)) for k in range(5): kk = np.random.randint(train_num) axes[0,k].set_axis_off() axes[0,k].imshow(f1_imgs[kk].squeeze(), cmap="gray") axes[1,k].set_axis_off() axes[1,k].imshow(f_msks[kk].squeeze(), cmap="gray") 



للوهلة الأولى ، كل شيء هو نفسه ، نفس الحذف ، نفس المضلعات ، لكن الشبكة بدأت تعمل بطريقة مختلفة تمامًا:

 Accuracy 1.0575 loss 0.0011 selected img 7963 tested img 9999 : : 17765it [29:02:00, 12.40s/it] 

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

ولكن ، كما يتضح من نتيجة العمل ، تسبب عامل تصفية gaussian_free في إنشاء العديد من المشكلات للشبكة ، وتولد الكثير من المعلومات الجديدة ، وربما الزائدة عن الحاجة.

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



تشير النتيجة إلى أن اللون العشوائي ليس إضافة بسيطة على الإطلاق.

 Accuracy 0.9004 loss 0.0315 selected img 251 tested img 9832 : : 1000it [06:46, 1.33it/s] 

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

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

إن إمكانية تطبيق مثال Lego هذا في الحالات الحقيقية أمر واضح ، لكن المدربين الحقيقيين وشبكات القراء متروك للقراء أنفسهم.

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


All Articles