内容简介:在上一篇文章《在这篇文章中,我们将说明如何读取HDF5文件,从头实现一个AlexNet网络模型。掌握了这些知识,你也可以去挑战一下Kaggle竞赛。需要指出的是,在ImageNet超大规模数据集上训练,特别是深度模型,非常耗时!!!周末在我的机器(配置为CPU: i7 6700, GPU: GTX960, MEM: 8G)上从头训练AlexNet模型,一个Epoch下来,都要花上一天的时间,结果搭上整个周末,也就跑了两个Epoch,更别提对比使用HDF5文件前后的效果。看样子该升级一下机器配置:(在实现Al
在上一篇文章《 深度学习中超大规模数据集的处理 》中讲到采用HDF5文件处理大规模数据集。有朋友问到: HDF5文件是一次性读入内存中,然后通过键进行访问吗 ?答案当然不是,在前面的文章中也提到过,最后生成的train.hdf5文件高达30G,如果全部加载到内存,内存会撑爆。实际上,由于HDF5采用了特殊的文件格式,这样我们可以在一次读操作中加载一个批量(比如128)的图片,而不用一个个的读取。也就是说采用这种方式,只是减少了IO操作次数,另外加载的图片是RAW图像数据,减少了解码时间。
在这篇文章中,我们将说明如何读取HDF5文件,从头实现一个AlexNet网络模型。掌握了这些知识,你也可以去挑战一下Kaggle竞赛。需要指出的是,在ImageNet超大规模数据集上训练,特别是深度模型,非常耗时!!!周末在我的机器(配置为CPU: i7 6700, GPU: GTX960, MEM: 8G)上从头训练AlexNet模型,一个Epoch下来,都要花上一天的时间,结果搭上整个周末,也就跑了两个Epoch,更别提对比使用HDF5文件前后的效果。看样子该升级一下机器配置:(
图像预处理
在实现AlexNet网络模型之前,先介绍几个图像预处理方法,这些预处理在计算机视觉深度学习中应用十分广泛,可以有效的提高图像分类的准确率。
-
均值减法(mean subtraction)
在上一篇文章中,在处理图像文件的过程中,计算了所有图像的RGB通道的均值。而所谓的均值减法预处理就是将图像的每个像素点RGB通道值减去对应通道的均值,简单说,公式如下:
比如一张狗狗的图片,经过均值预处理,得到了右边的图像。
均值减法是一种数据归一化技巧,可以减少光线变化造成的影响。
借助于opencv的处理函数,实现均值减法非常简单:
(B, G, R) = cv2.split(image.astype("float32")) # subtract the means for each channel R -= self.r_mean G -= self.g_mean B -= self.b_mean # merge the channels back and return the image return cv2.merge([B, G, R])
-
切片(patch)
切片预处理就是在训练过程中随机截取图像M x N区域内的像素值。我们知道,CNN模型要求图像输入尺寸是一个固定值,如果我们使用的图像大小和输入尺寸不一致,通常的处理方法是对图像进行缩放。但是,如果使用的图像比输入尺寸大,还有一种更好的方法就是进行随机截取部分图像,这可以有效的降低过拟合。
上图中,随机裁剪256x256的图像到227x227大小。因为是随机裁剪,所以网络每次训练的图像不同,相当于一种数据扩充技术,可以减少过拟合。
我们也不需要从头实现,借助与sklearn中的实用函数,一句话就可以搞定:
def preprocess(self, image): # extract a random crop from the image with the target width and height return extract_patches_2d(image, (self.width, self.height), max_patches=1)[0]
-
裁切(cropp)
裁切预处理有点类似上面的切片预处理。不过有两点不同:
-
本预处理应用于验证数据集,而切片预处理应用在训练数据上。
-
本预处理固定截取4个角及正中间区域,在加上水平翻转,这样每张图片可以得到10张采样。
还记得《 提高模型准确率:组合模型 》这篇文章讲到,通过组合多个网络的输出可以提高分类准确率,这里就是计算10张采样的分类概率平均值,从而达到提高分类准确率的效果。
该预处理没有现成的函数可用,不过写起来也不难:
def preprocess(self, image): crops = [] # grab the width and height of the image then use these dimensions to # define the corners of the image based (h, w) = image[:2] coords = [ [0, 0, self,width, self.height], [w - self.width, 0, w, self.height], [w - self.width, h - self.height, w, h], [0, h - self.height, self.width, h] ] # compute the center crop of the image as well dw = int(0.5 * (w - self.width)) dh = int(0.5 * (h - self.height)) coords.append([dw, dh, w - dw, h - dh]) for (startx, starty, endx, endy) in coords: crop = image[startx:endx, starty:endy] crop = cv2.resize(crop, (self.width, self.height), interpolation=self.inter) crops.append(crop) if self.horiz: # compute the horizontal mirror flips for each crop mirrors = [cv2.flip(c, 1) for c in crops] crops.extend(mirrors) return np.array(crops)
HDF5数据集生成器
《深度学习中超大规模数据集的处理》中,我们将数据集存成HDF5文件格式,这里,我们需要从HDF5文件中按照批次读取图像数据及类别标签。
# loop over the HDF5 dataset for i in np.arange(0, self.num_images, self.batch_size): # extract the images and labels from HDF5 dataset images = self.db["images"][i : i + self.batch_size] labels = self.db["labels"][i : i + self.batch_size] if self.binarize: labels = np_utils.to_categorical(labels, self.classes) if self.preprocessors is not None: proc_images = [] for image in images: for p in self.preprocessors: image = p.preprocess(image) proc_images.append(image) images = np.array(proc_images)
每次读取batch_size个图像数据和类别标签,并进行预处理,代码中对images和labels的访问有点类似数组,[i : i + self.batch_size]读取第i到第(i+batch_size)个元素。
AlexNet
相对于我们之前实现的深度学习模型,AlexNet相当复杂,图的层如下表所示:
当AlexNet首次提出来时,还没有出现批量归一化等技术。在实现中,我们将在激活后加入批量归一化,对于使用卷积神经网络的大多数图像分类任务而言,这是非常标准的处理。另外,我们还会在每次POOL操作后做一些dropout,以进一步减少过拟合。
在前面的文章中,我们已经见识过keras的简洁之处,即使是AlexNet这样复杂的网络,对keras而言只是多几行代码而已。
class AlexNet: @staticmethod def build(width, height, depth, classes, reg=0.0002): model = Sequential() input_shape = (width, height, depth) channel_dim = -1 if K.image_data_format() == "channels_first": input_shape = (depth, width, height) channel_dim = 1 # block #1: CONV => RELU => POOL model.add(Conv2D(96, (11, 11), strides=(4, 4), input_shape=input_shape, padding="same", kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization(axis=channel_dim)) model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2))) model.add(Dropout(0.25)) # block #2: CONV => RELU => POOL model.add(Conv2D(256, (5, 5), padding="same", kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization(axis=channel_dim)) model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2))) model.add(Dropout(0.25)) # block #3: CONV => RELU => CONV => RELU => CONV => RELU => POOL model.add(Conv2D(384, (3, 3), padding="same", kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization(axis=channel_dim)) model.add(Conv2D(384, (3, 3), padding="same", kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization(axis=channel_dim)) model.add(Conv2D(256, (3, 3), padding="same", kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization(axis=channel_dim)) model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 2))) model.add(Dropout(0.25)) # block #4: FC => RELU model.add(Flatten()) model.add(Dense(4096, kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization()) model.add(Dropout(0.5)) # block #5: FC => RELU model.add(Dense(4096, kernel_regularizer=l2(reg))) model.add(Activation("relu")) model.add(BatchNormalization()) model.add(Dropout(0.5)) # softmax classifier model.add(Dense(classes, kernel_regularizer=l2(reg))) model.add(Activation("softmax")) return model
接下来就是训练和测试模型,对于深度学习而言,不管是复杂还是简单的模型,其训练和测试过程都是大同小异,所以在这里我也不再罗嗦,有兴趣的同学可以参考我在github上的完整代码。但是需要注意,这个训练非常耗时,如果没有极其牛的显卡,还是不要轻易尝试。其实对于图片分类任务来说,最好还是采用迁移学习,站在巨人的肩膀上,不仅省力,效果还更好一些。
以上实例均有完整的代码,点击阅读原文,跳转到我在github上建的示例代码。
另外,我在阅读《Deep Learning for Computer Vision with Python》这本书,在微信公众号后台回复“计算机视觉”关键字,可以免费下载这本书的电子版。
往期回顾
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- React怎样从函数中辨别类
- MSP和CMP,辨别一二
- AI神经网络如何辨别事物
- 漫画:如何辨别二逼互联网公司!?
- 压缩算法 bzip2 的官网 bzip.org 域名过期,请注意辨别
- 竞赛推荐 | ChinaMM2019 竞赛-1:水下图像增强
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。