原语的简单性和复杂性,或者如何确定神经网络不必要的预处理

这是关于椭圆,三角形和其他几何形状的分析和研究的第三篇文章。
先前的文章在读者中提出了一些非常有趣的问题,尤其是有关某些训练序列的复杂性或简单性的问题。 这些问题实际上非常有趣,例如,三角形比四边形或其他多边形难学习得多?



让我们尝试进行比较,为了进行比较,我们有一个很棒的想法,经过几代学生的检验,这个想法-备忘单越短,考试越容易。

这篇文章也仅仅是好奇心和闲置兴趣的结果,在实践中一无所获,对于实际的任务,有很多不错的主意,但是几乎没有复制粘贴的内容。 这是对训练序列的复杂性的小研究-提供了作者的推理和代码,您可以自己检查/补充/更改所有内容。

因此,让我们尝试找出哪个几何图形更复杂或更容易分割,哪个AI讲座课程更容易理解和吸收。

有许多不同的几何形状,但是我们仅比较三角形,四边形和五角星。 我们将使用一种简单的方法来构建火车序列-将128x128的单色图像分为四个部分,并在这些区域中随机放置一个椭圆和一个三角形。 我们将检测与椭圆颜色相同的三角形。 即 任务是训练网络以区分例如四边形多边形和以相同颜色绘制的椭圆。 这是我们将要研究的图片示例







我们不会在一张图片中检测到三角形和四边形,而是在椭圆形干涉的背景下,在不同的列中分别检测它们。

让我们以经典的U-net和带有三角形,四边形和星形的三种类型的训练序列进行研究。

因此,鉴于:

  • 图片/遮罩对的三个训练序列;
  • 网络。 普通的U-net,广泛用于细分。

测试思路:

  • 确定哪些训练序列“更难”学习;
  • 一些预处理技术如何影响学习

让我们开始,选择10,000对带有椭圆形和遮罩的四边形图片,并仔细考虑它们。 我们对婴儿床将要走多短以及长度的长短感兴趣。

我们加载库,确定图片数组的大小
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 }) 


我们将使用第一篇文章中的指标。 让我提醒读者,我们将预测像素的遮罩-这是“背景”或“四边形”,并评估预测的真伪。 即 以下四个选项是可能的-我们正确地预测像素是背景,正确地预测像素是四边形,或者在预测“背景”或“四边形”时出错。 因此,对于所有图片和所有像素,我们估计所有四个选项的数量并计算结果-这将是网络的结果。 错误的预测越少越准确,结果越准确,网络越好。

我们将网络视为一个“黑匣子”,我们不会研究网络内部发生的情况,权重如何变化以及如何选择渐变-我们稍后将在比较网络时调查网络的肠胃。

简单的U网
 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() 


生成图片/遮罩对的功能。 在黑白图片128x128上,它填充有随机噪声,且随机噪声是从两个范围(即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张图片。 根据所选的数据集和准确性,网络训练可以持续很长时间。 但是我们只有几次迭代。 我再说一遍,对于我们现在来说网络学习或学习的方式和知识并不重要,主要是它已达到既定的预测准确性。

现在开始主要实验


我们将构建备忘单,我们将为所有三个训练序列分别构建备忘单并比较它们的长度。 我们将从构建的序列中获取新的图片/遮罩对,并尝试由受过训练的网络在已选择的序列上进行预测。 刚开始时,只有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_filter会为网络带来很多问题,并会产生很多新的信息,并且可能是多余的信息。

好吧,对于最单纯的简约爱好者,我们将多边形替换为相同的椭圆,但颜色没有任何随机性



结果表明,随机颜色根本不是简单的加法。

 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/zh-CN439122/


All Articles