计算机视觉的深度学习

我们将深入探讨卷积的原理以及为什么它们在计算机视觉任务中如此成功。但首先,让我们来看看一个非常简单的“convnet”示例,我们将使用我们的convnet来对MNIST数字进行分类。

下面的6行代码展示了基本的convnet是什么样子的。它是一系列 Conv 2d和MaxPooling2D层。我们马上就会看到他们具体做了什么。重要的是,一个convnet作为形状的输入张量(image_height, image_width, image_channel)(不包括批处理维度)。

我们的convnet处理大小的输入(28,28,1),这是MNIST图像的格式。我们通过将参数input_shape=(28,28,1)传递到我们的第一层。

实例化一个小的卷积层

from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

展示网络层:

>>> model.summary()
________________________________________________________________
Layer (type) Output Shape Param #
================================================================
conv2d_1 (Conv2D) (None, 26, 26, 32) 320
________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (None, 13, 13, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (None, 11, 11, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (None, 5, 5, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (None, 3, 3, 64) 36928
================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0

你可以在上面看到,每个Conv2D和MaxPooling2D层的输出都是一个三维的形状张量(高度、宽度、通道)。随着网络的深入,宽度和高度都会缩小。通道的数量由传递给Conv2D层的第一个参数控制(例如32或64)。

一步是将我们的最后一个输出张量(形状(3,3,64))输入到一个与你已经熟悉的、紧密相连的分类器网络中:一堆全连接层。这些分类器处理向量,它们是1D,而我们现在的输出是一个3D张量。首先,我们要把3D输出压平到1D,然后在上面再加一些稠密的层。

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))>>> model.summary()
Layer (type) Output Shape Param #
================================================================
conv2d_1 (Conv2D) (None, 26, 26, 32) 320
________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (None, 13, 13, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (None, 11, 11, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (None, 5, 5, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (None, 3, 3, 64) 36928
________________________________________________________________
flatten_1 (Flatten) (None, 576) 0
________________________________________________________________
dense_1 (Dense) (None, 64) 36928
________________________________________________________________
dense_2 (Dense) (None, 10) 650
================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0

正如你所看到的,我们的(3,3,64)输出被压平成形状的矢量(576,),然后输入全连接层。

from keras.datasets import mnist
from keras.utils import to_categorical
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype('float32') / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype('float32') / 255
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=5, batch_size=64)>>> test_loss, test_acc = model.evaluate(test_images, test_labels)
>>> test_acc
>0.99080000000000001

卷积操作

经过卷积操作后提取的特征如下:
这里写图片描述
卷积操作有两个有趣的特性:

  1. 卷积操作学习的模式是平移不变的,也就是说,在学习了图片右下角的某个模式后,一个convnet可以在任何地方识别它,例如在左上角。对于全连接网络,如果图片特征出现在一个新的位置,那么它就必须重新学习这个模式。这使得在处理图像时,convnets的数据效率非常高(因为visual world基本上是平移不变的):它们需要较少的训练样本来学习具有泛化能力的表示。
  2. 它们可以学习模式的空间层次结构。第一个卷积层将学习小的局部模式,例如边缘,但是第二个卷积层将学习由第一个层的特征构成的更大的模式。等等。这使得convnets能够有效地学习越来越复杂和抽象的视觉概念(因为视觉世界从根本上是空间层次)。
    这里写图片描述
    卷积操作超过3D张量,称为“feature maps”,有两个空间轴(“高度”和“宽度”)以及一个“深度”轴(也称为“通道”轴)。对于RGB图像,“深度”轴的尺寸为3,因为图像有3个颜色通道,红色、绿色和蓝色。对于黑白图像,就像我们的MNIST数字一样,深度只有1(灰度)。卷积操作从它的输入特性映射中提取补丁,并对所有这些补丁应用相同的转换,生成一个输出特性映射。这个输出特性图仍然是一个3D张量:它仍然有一个宽度和一个高度。它的深度可以是任意的,因为输出深度是层的参数,而深度轴的不同通道不再代表特定的颜色,比如RGB输入,它们代表的是我们所说的filters。Filters 对输入数据的特定方面进行编码:例如,在高级别上,单个 filter 可以对“在输入中出现的人脸”进行编码。
    在我们的MNIST示例中,第一个卷积层采用大小(28、28、1)的feature map,并输出一个(26,26,32)大小的feature map,即它在其输入上计算32个“filters”。这32个输出通道中的每一个都包含一个26x26的值网格,这是一个 filter 对输入的“响应映射”,指示在输入的不同位置的 filer 模式的响应。这就是术语“feature map”的真正含义:深度轴的每个维度都是一个特性(或 filer ),而2D张量输出[:,n]是这个过滤器对输入的响应的二维空间“映射”。
    这里写图片描述

卷积层由两个关键参数决定:

  1. 从输入中提取的补丁的大小(通常为3x3或5x5)。在我们的例子中,它总是3x3,这是一个很常见的选择。
  2. 输出特征图的深度,即由卷积计算的 filers 数。在我们的示例中,我们以32的深度开始,以64的深度结束。

在Keras Conv2D层中,这些参数是传递给层的第一个参数:Conv2D(output_depth, (window_height, window_width))。
通过“滑动”这些大小为3x3或5x5的窗口,通过3D输入feature map,在每个可能的位置停止,并提取周围特性的3D补丁(形状(window_height, window_width, input_depth))。然后,每个这样的3D贴片被转换(通过一个具有相同学习权矩阵的张量乘积,称为“卷积核”)到一个一维的形状向量(output_depth,)。然后将所有这些向量空间重新组合成一个形状(高度、宽度、output_depth)的3D输出图。输出特性映射中的每个空间位置都对应于输入特性映射中的相同位置(例如,输出的右下角包含输入的右下角的信息)。例如,在3x3窗口中,矢量输出[i, j,:]来自3D补丁输入[i-1:i+1, j-1:j+1,:]。
这里写图片描述

注意,输出的宽度和高度可能与输入的宽度和高度不同。他们可能因两个原因而不同:

  1. 边界效应,可以通过填充输入特征图来抵消
  2. “strides”的使用

理解边界效应和填充

考虑一个5x5的feature map(总共25块)。只有9个不同的瓦片,你可以把它放在一个3x3的窗口中,形成一个3x3的网格。因此,输出feature map将是3x3:它会缩小一点,在这个例子中,每一个维度旁边正好有两个方块。您可以在上面的示例中看到这种“边界效应”:我们从28x28输入开始,在第一个卷积层之后,它变成了26x26。
这里写图片描述
如果您想要获得与输入相同的空间维度的输出特性图,则可以使用 Padding:
这里写图片描述
在Conv2D层中,填充可以通过padding参数进行配置,该参数采用两个值:“valid”,即没有填充(只使用“valid”窗口位置)和“same”,这意味着“以相同宽度和高度的输出作为输入”,填充参数默认为“valid”。

理解卷积的Strides

这里写图片描述
使用stride 2意味着feature map的宽度和高度被一个因子2(除了边界效应所引起的任何变化)所降低。在实践中很少使用带条纹的卷积,尽管它们在某些类型的模型中很有用,但是熟悉这个概念通常很好。

The max pooling operation

在我们的convnet示例中,您可能已经注意到,每个MaxPooling2D层之后,feature map的大小都会减半。例如,在第一个MaxPooling2D层之前,feature map是26x26,但最大池操作将其分为13x13。这就是max pooling的作用:大幅降低样本特征映射,就像跨越的卷积。

a convnet without pooling layers

model_no_max_pool = models.Sequential()
model_no_max_pool.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activation='relu'))
model_no_max_pool.add(layers.Conv2D(64, (3, 3), activation='relu'))>>> model_no_max_pool.summary()
Layer (type) Output Shape Param #
================================================================
conv2d_4 (Conv2D) (None, 26, 26, 32) 320
________________________________________________________________
conv2d_5 (Conv2D) (None, 24, 24, 64) 18496
________________________________________________________________
conv2d_6 (Conv2D) (None, 22, 22, 64) 36928
================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0

这一步有什么问题,主要有两方面:

  1. 它不利于学习特征的空间层次结构。第三层的3x3窗口只包含来自初始输入的7x7窗口的信息。我们的convnet所学习的高级模式在初始输入方面仍然非常小,这可能还不足以学习对数字进行分类(仅通过7x7像素的窗口来识别一个数字)。我们需要从上一个卷积层的特征来包含关于输入全部的信息。
  2. 最终的feature map有222264 = 31000总系数。这是巨大的。如果我们把它压平,在上面粘上512大小的全连接层,那层就会有1580万个参数。对于这样一个小模型来说,这太大了,会导致过度拟合。

简而言之,使用downsampling的原因仅仅是为了减少特征映射系数的处理次数,以及通过连续的卷积层观察越来越大的窗口来诱导空间过滤层次结构(就其所覆盖的原始输入的分数而言)。


深度学习与小数据的问题

你有时会听到,只有当大量数据可用时,深度学习才会起作用。这在一定程度上是一个有效的观点:深度学习的一个基本特征是,它能够在自己的训练数据中找到有趣的特性,而不需要人工提取的特征工程,只有当大量的训练实例可用时才能实现。对于输入样本非常高维的问题,比如图像,尤其如此。

然而,对于初学者来说,“大量”的样本是相对于你想要训练的网络的大小和深度而言的。仅仅用几十个样本就可以训练一个Convnets网络来解决一个复杂的问题是不可能的,但是如果这个模型是小的,并且有良好的正则化,并且任务简单,那么几百个可能就足够了。

但更重要的是,深度学习模型本质上是高度可重用的。比如,你可以在一个大规模数据集上接受一个图像分类或语音到文本的模型,然后在一个非常不同的问题上重用它,只需要做一些细微的改变。具体地说,在计算机视觉的情况下,许多预先训练过的模型(通常是在ImageNet数据集上进行训练)现在可以公开下载,并且可以通过非常小的数据来引导强大的视觉模型。

Copying images to train,validation and test directories

import os, shutil 
# The path to the directory where the original
# dataset was uncompressed
original_dataset_dir = '/Users/Downloads/kaggle_original_data'# The directory where we will
# store our smaller dataset
base_dir = '/Users/Downloads/cats_and_dogs_small'
os.mkdir(base_dir)# Directories for our training,
# validation and test splits
train_dir = os.path.join(base_dir, 'train')
os.mkdir(train_dir)
validation_dir = os.path.join(base_dir, 'validation')
os.mkdir(validation_dir)
test_dir = os.path.join(base_dir, 'test')
os.mkdir(test_dir)# Directory with our training cat pictures
train_cats_dir = os.path.join(train_dir, 'cats')
os.mkdir(train_cats_dir)# Directory with our training dog pictures
train_dogs_dir = os.path.join(train_dir, 'dogs')
os.mkdir(train_dogs_dir)# Directory with our validation cat pictures
validation_cats_dir = os.path.join(validation_dir, 'cats')
os.mkdir(validation_cats_dir)# Directory with our validation dog pictures
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
os.mkdir(validation_dogs_dir)# Directory with our test cat pictures
test_cats_dir = os.path.join(test_dir, 'cats')
os.mkdir(test_cats_dir)# Directory with our test dog pictures
test_dogs_dir = os.path.join(test_dir,'dogs')
os.mkdir(test_dogs_dir)# Copy first 1000 cat images to train_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(train_cats_dir, fname)shutil.copyfile(src, dst)# Copy next 500 cat images to validation_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(validation_cats_dir,fname)shutil.copyfile(src, dst)# Copy next 500 cat images to test_cats_dir
fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(test_cats_dir, fname)shutil.copyfile(src, dst)# Copy first 1000 dog images to train_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(train_dogs_dir, fname)shutil.copyfile(src, dst)# Copy next 500 dog images to validation_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(validation_dogs_dir, fname)shutil.copyfile(src, dst)# Copy next 500 dog images to test_dogs_dir
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:src = os.path.join(original_dataset_dir, fname)dst = os.path.join(test_dogs_dir, fname)shutil.copyfile(src, dst)

Counting the images


>>> print('total training cat images:', len(os.listdir(train_cats_dir)))
total training cat images: 1000
>>> print('total training dog images:', len(os.listdir(train_dogs_dir)))
total training dog images: 1000
>>> print('total validation cat images:', len(os.listdir(validation_cats_dir)))
total validation cat images: 500
>>> print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
total validation dog images: 500
>>> print('total test cat images:', len(os.listdir(test_cats_dir)))
total test cat images: 500
>>> print('total test dog images:', len(os.listdir(test_dogs_dir)))
total test dog images: 500

搭建网络

注意,feature map的深度在网络中逐渐增加(从32到128),而feature map的大小正在减少(从148x148到7x7)。这是一个在几乎所有的Convnets 中都能看到的模式。

from keras import layers
from keras import modelsmodel = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

让我们来看看特征映射的维度是如何随着每一个连续的层而变化的。


>>> model.summary()
Layer (type) Output Shape Param #
================================================================
conv2d_1 (Conv2D) (None, 148, 148, 32) 896
________________________________________________________________
maxpooling2d_1 (MaxPooling2D) (None, 74, 74, 32) 0
________________________________________________________________
conv2d_2 (Conv2D) (None, 72, 72, 64) 18496
________________________________________________________________
maxpooling2d_2 (MaxPooling2D) (None, 36, 36, 64) 0
________________________________________________________________
conv2d_3 (Conv2D) (None, 34, 34, 128) 73856
________________________________________________________________
maxpooling2d_3 (MaxPooling2D) (None, 17, 17, 128) 0
________________________________________________________________
conv2d_4 (Conv2D) (None, 15, 15, 128) 147584
________________________________________________________________
maxpooling2d_4 (MaxPooling2D) (None, 7, 7, 128) 0
________________________________________________________________
flatten_1 (Flatten) (None, 6272) 0
________________________________________________________________
dense_1 (Dense) (None, 512) 3211776
________________________________________________________________
dense_2 (Dense) (None, 1) 513
================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0

编译网络

from keras import optimizers
model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

数据处理

正如现在已经知道的,在将数据输入到我们的网络之前,数据应该被格式化为适当的预处理的浮点张量。目前,我们的数据以JPEG文件的形式存在,因此将其放入我们的网络的步骤大致是这样的:

  1. 读取图片文件。
  2. 将JPEG内容解码为像素的RBG网格。
  3. 将它们转换为浮点张量。
  4. 将像素值(从0到255)重新缩放到[0,1]区间 (如你所知,神经网络更喜欢处理小的输入值)。

Keras与图像处理模块的辅助工具,位于keras.preprocessing.image。特别地,它包含类ImageDataGenerator,它允许快速设置Python生成器,可以自动将磁盘上的图像文件转换为成批的预处理的张量。这就是我们要用到的。

用 ImageDataGenerator 读取图片

from keras.preprocessing.image import ImageDataGenerator# All images will be rescaled by 1./255
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory(# This is the target directorytrain_dir,# All images will be resized to 150x150target_size=(150, 150),batch_size=20,# Since we use binary_crossentropy loss, we need binary labelsclass_mode='binary')# categoricalvalidation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=20,class_mode='binary')

让我们来看看其中一个生成器的输出:它生成了150×150的RGB图像(形状(20、150、150、3))和二分类标签(形状(20,))。20是每批样品的数量(batch_size)。请注意,生成器无限地生成这些批次:它只是对目标文件夹中的图像无休止地循环。出于这个原因,我们需要在某个时刻打破迭代循环。

Displaying the shapes of a batch of data and labels

>>> for data_batch, labels_batch in train_generator:
>>>    print('data batch shape:', data_batch.shape)
>>>    print('labels batch shape:', labels_batch.shape)
>>>    break
data batch shape: (20, 150, 150, 3)
labels batch shape: (20,)

让我们使用生成器将模型与数据相匹配。我们使用的是fit_generator方法,它相当于我们的数据生成器。它希望作为第一个参数,一个Python生成器能够像我们一样,无限期地产生大量的输入和目标。由于数据是不断生成的,因此生成器需要知道从生成器中抽取多少个样本,然后才宣告一个时代结束。这是steps_per_epoch参数的角色:在从生成器中提取steps_per_epoch批次之后,即在运行steps_per_epoch梯度下降步骤之后,拟合过程将进入下一个阶段。在我们的情况下,批次是20个样品,所以要100个批次,直到我们看到我们的2000个样品的目标。

当使用fit_generator时,您可能会传递validation_data参数,这与fit方法非常相似。重要的是,这个参数可以是一个数据生成器本身,但是它也可以是一组Numpy数组。如果您将生成器作为validation_data传递,那么这个生成器将会不断地生成批次的验证数据,因此您还应该指定validation_steps参数,该参数告诉流程从验证生成器中抽取多少批次进行评估。

history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)

训练后总是保存模型是很好的习惯。

model.save('cats_and_dogs_small_1.h5')

在训练过程中,让我们在训练和验证数据上画出模型的损失和准确性。

import matplotlib.pyplot as pltacc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

这里写图片描述
这里写图片描述
因为我们只有相对较少的培训样本(2000),解决过拟合将是首选。你已经了解了一些可以帮助减轻过拟合的技术,例如Drop out和weight decay(L2正则化)。我们现在将介绍一种新的,具体到计算机视觉,并且在处理深度学习模型的图像时几乎普遍使用:数据增强。

数据增强

过度拟合是由于样本太少,无法学习,使我们无法训练一个能够推广到新数据的模型。数据增强的方法是从现有的训练样本中生成更多的训练数据,通过一些随机的转换来“增加”样本,这些转换产生可信的图像。我们的目标是在训练的时候,我们的模型永远不会看到完全相同的画面。这有助于模型更深入地了解数据的各个方面,并更好地推广。

在Keras中,可以通过在ImageDataGenerator实例读取的图像上配置一些随机转换来实现这一点。

datagen = ImageDataGenerator(rotation_range=40,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True,fill_mode='nearest')

这些只是一些可用的选项(更多的,请参见Keras文档)。回顾一下这些参数:

  1. rotation_range:取值在(0,180),随机旋转图片的范围。
  2. width_shift and height_shift:取值范围(作为总宽度或高度的一小部分)是在其中任意地垂直或水平地转换图片。
  3. shear_range:是随机应用剪切变换。
  4. zoom_range:是在图片中随机放大。
  5. horizontal_flip: for randomly flipping half of the images horizontally—relevant when there are no assumptions of horizontal asymmetry (e.g. real-world pictures).
  6. fill_mode: is the strategy used for filling in newly created pixels, which can appear after a rotation or a width/height shift.
# This is module with image preprocessing utilities
from keras.preprocessing import imagefnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]# We pick one image to "augment"
img_path = fnames[3]# Read the image and resize it
img = image.load_img(img_path, target_size=(150, 150))
# Convert it to a Numpy array with shape (150, 150, 3)
x = image.img_to_array(img)# Reshape it to (1, 150, 150, 3)
x = x.reshape((1,) + x.shape)# The .flow() command below generates batches of randomly transformed images.
# It will loop indefinitely, so we need to `break` the loop at some point!
i = 0
for batch in datagen.flow(x, batch_size=1):plt.figure(i)imgplot = plt.imshow(image.array_to_img(batch[0]))i += 1if i % 4 == 0:breakplt.show()

这里写图片描述

如果我们使用这个数据扩充配置来训练一个新的网络,我们的网络将不会看到两次相同的输入。但是,它所看到的输入仍然是相互关联的,因为它们来自于少数原始图像,我们不能产生新的信息,我们只能重新组合现有的信息。因此,这可能不足以完全消除过度拟合。为了进一步打击过度拟合,我们还将在我们的模型中添加一个Drop out,在全连接的分类器之前:

Defining a new convnet that includes dropout

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

Training our convnet using data augmentation generators

train_datagen = ImageDataGenerator(rescale=1./255,rotation_range=40,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True,)# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory(# This is the target directorytrain_dir,# All images will be resized to 150x150target_size=(150, 150),batch_size=32,# Since we use binary_crossentropy loss, we need binary labelsclass_mode='binary')validation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=32,class_mode='binary')history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=100,validation_data=validation_generator,validation_steps=50)

让我们保存我们的模型,我们将在Convnet网络可视化部分使用它。

model.save('cats_and_dogs_small_2.h5')

这里写图片描述
这里写图片描述
由于数据的增加和Drop out,我们不再过拟合:训练曲线更接近于验证曲线。我们现在能够达到82%的精确度,相对于非正则化模型有15%的相对改进。

使用预先训练的卷积网络

对小图像数据集进行深度学习的一种常见且非常有效的方法是利用预先训练好的网络。一个预先训练的网络只是一个以前在大数据集上训练的保存的网络,通常是大规模的图像分类任务。

VGG16模型和其他模型一样,都是预先打包的Keras,你可以从 keras.applications 导入它。这里是图像分类模型的列表(都是在ImageNet数据集上预先训练的),作为keras.applications的一部分可用:
这里写图片描述

实例化模型

from keras.applications import VGG16conv.base = VGG16(weights = 'imagenet',include_top = False, input_shape=(150,150,3))

函数中传入了三个参数:
**1. weights ?*指定哪个权重检查点来初始化模型。
**2. include_top:**确定是否包括网络中的全连接层,由于原网络中的全连接层会从ImageNet中分出1000类,而在这里我们只做二分类任务,所以不适用。
**3. input_shape:**输入网络的图像张量,如果我们不定义,网络可以处理任何尺寸的输入。

展示VGG16的网络模型:

>>> conv_base.summary()
Layer (type) Output Shape Param #
================================================================
input_1 (InputLayer) (None, 150, 150, 3) 0
________________________________________________________________
block1_conv1 (Convolution2D) (None, 150, 150, 64) 1792
________________________________________________________________
block1_conv2 (Convolution2D) (None, 150, 150, 64) 36928
________________________________________________________________
block1_pool (MaxPooling2D) (None, 75, 75, 64) 0
________________________________________________________________
block2_conv1 (Convolution2D) (None, 75, 75, 128) 73856
________________________________________________________________
block2_conv2 (Convolution2D) (None, 75, 75, 128) 147584
________________________________________________________________
block2_pool (MaxPooling2D) (None, 37, 37, 128) 0
________________________________________________________________
block3_conv1 (Convolution2D) (None, 37, 37, 256) 295168
________________________________________________________________
block3_conv2 (Convolution2D) (None, 37, 37, 256) 590080
________________________________________________________________
block3_conv3 (Convolution2D) (None, 37, 37, 256) 590080
________________________________________________________________
block3_pool (MaxPooling2D) (None, 18, 18, 256) 0
________________________________________________________________
block4_conv1 (Convolution2D) (None, 18, 18, 512) 1180160
________________________________________________________________
block4_conv2 (Convolution2D) (None, 18, 18, 512) 2359808
________________________________________________________________
block4_conv3 (Convolution2D) (None, 18, 18, 512) 2359808
________________________________________________________________
block4_pool (MaxPooling2D) (None, 9, 9, 512) 0
________________________________________________________________
block5_conv1 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_conv2 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_conv3 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_pool (MaxPooling2D) (None, 4, 4, 512) 0
================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0

最后feature map的形状为(4,4,512),这就是我们将在上面连接一个全连接分类器的输入特性。

在这一点上,有两种方法可以进行:

  1. 在数据集上运行卷积基础,将其输出记录到一个Numpy数组中,然后将这些数据作为输入,作为全连接层分类器的输入。这个解决方案是非常快且方便的,因为它只需要为每个输入图像运行一次卷积基础,而卷积基是目前为止网络中最负责的部分。然而,出于同样的原因,这种技术不允许我们利用数据增强。
  2. 通过在添加全连接层来扩展模型(conv_base),并在输入数据上运行整个端到端。这允许我们使用数据增强,因为每次输入图像都是通过模型看到的卷积基。然而,出于同样的原因,这种技术要比第一个技术负责得多。

我们将介绍这两种技术。让我们遍历设置第一种方法所需的代码:记录我们数据上的conv_base的输出,并将这些输出作为新模型的输入。

Extracting features using the pre-trained convolutional base

import os
import numpy as np
from keras.preprocessing.image import ImageDataGeneratorbase_dir = '/Users/fchollet/Downloads/cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20def extract_features(directory, sample_count):features = np.zeros(shape=(sample_count, 4, 4, 512))labels = np.zeros(shape=(sample_count))generator = datagen.flow_from_directory(directory,target_size=(150, 150),batch_size=batch_size,class_mode='binary')i = 0for inputs_batch, labels_batch in generator:features_batch = conv_base.predict(inputs_batch)features[i * batch_size : (i + 1) * batch_size] = features_batchlabels[i * batch_size : (i + 1) * batch_size] = labels_batchi += 1if i * batch_size >= sample_count:# Note that since generators yield data indefinitely in a loop,# we must `break` after every image has been seen once.breakreturn features, labelstrain_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)

提取的特征目前是形状(samples,4,4,512)。我们将会把它们输出给全连接层,所以首先我们必须把它们压平为(samples,8192)。

train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))

此时,我们要定义一个全连接层,注意使用Drop out来正则化,并在刚才提取的特征上训练数据:

from keras import models
from keras import layers
from keras import optimizers
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),loss='binary_crossentropy',metrics=['acc'])history = model.fit(train_features, train_labels,epochs=30,batch_size=20,validation_data=(validation_features, validation_labels))

训练是非常快的,因为我们只需要处理两个全连接层,一个epoch 即使在CPU上也只需要不到一秒的时间。

import matplotlib.pyplot as pltacc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()plt.figure()plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()plt.show()

这里写图片描述
我们达到了大约90%的验证精度,比我们在上一节中通过从头开始训练的小模型所能达到的效果要好得多。然而,结果也表明,尽管我们Drop out的值相当高,但我们几乎从一开始就过拟合。这是因为该技术不利用数据增强,这对于防止小型图像数据集的过度拟合是至关重要的。

Adding a densely-connected classifier on top of the convolutional base

因为模型的行为就像层一样,你可以将一个模型(比如我们的conv_base)添加到一个序列模型中,就像你添加一个层一样。你可以这样做:

from keras import models
from keras import layersmodel = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

模型现在是这个样子:

>>> model.summary()
Layer (type) Output Shape Param #
================================================================
vgg16 (Model) (None, 4, 4, 512) 14714688
________________________________________________________________
flatten_1 (Flatten) (None, 8192) 0
________________________________________________________________
dense_1 (Dense) (None, 256) 2097408
________________________________________________________________
dense_2 (Dense) (None, 1) 257
================================================================
Total params: 16,812,353
Trainable params: 16,812,353
Non-trainable params: 0

如你所见,VGG16的卷积基础有14,714,688个参数,非常大。我们在上面添加的分类器有200万个参数。

在我们编译和训练我们的模型之前,要做的一件非常重要的事情是***冻结卷积基***。“Freezing”一层或一套层意味着在训练期间防止他们的weight得到更新。如果我们不这样做,那么之前在卷积基础上学习的表示会在训练过程中被修改。由于顶部的全连接层是随机初始化的,所以非常大的权重更新将通过网络传播,从而会破坏之前卷积学到的内容。

>>> print('This is the number of trainable weights ''before freezing the conv base:', len(model.trainable_weights))
This is the number of trainable weights before freezing the conv base: 30
>>> conv_base.trainable = False
>>> print('This is the number of trainable weights ''after freezing the conv base:', len(model.trainable_weights))
This is the number of trainable weights after freezing the conv base: 4

有了这个设置,只有我们添加的两个全连接层的权重将被训练。这是一个总共有4个分量的张量:每层2个(主要的权重矩阵和偏置向量)。请注意,为了使这些更改生效,我们必须首先编译模型。如果您在编译后修改了weight trainability,那么应该重新编译模型,否则这些更改将被忽略。

from keras.preprocessing.image import ImageDataGeneratortrain_datagen = ImageDataGenerator(rescale=1./255,rotation_range=40,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True,fill_mode='nearest')# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory(# This is the target directory
train_dir,# All images will be resized to 150x150target_size=(150, 150),batch_size=20,# Since we use binary_crossentropy loss, we need binary labelsclass_mode='binary')validation_generator = test_datagen.flow_from_directory(validation_dir,target_size=(150, 150),batch_size=20,class_mode='binary')model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=2e-5),metrics=['acc'])history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=30,validation_data=validation_generator,validation_steps=50)

这里写图片描述


Fine - tuning

另一种广泛使用的用于模型重用的技术是fine-tuning。fine-tuning包括解冻用于特征提取的冻结模型库的几个顶层,并联合培训新添加的模型(在我们的示例中是全连接的分类器)和这些顶层。这被称为“Fine - tuning”,因为它稍微调整了被重用模型的更抽象表示,以便使它们更适合于手头的问题。
这里写图片描述
我们之前已经说过,为了能够在上面训练一个随机初始化的分类器,需要冻结VGG16的卷积基础。出于同样的原因,一旦上面的分类器已经被训练,它只可能调整卷积基础的顶层。如果分类没有经过训练,那么在训练过程中通过网络传播的错误信号将会太大,而之前被调整的层所得到的表示将被破坏。因此,对网络进行Fine - tuning 的步骤如下:

  1. 在已经训练好的基础网络上加自定义的网络
  2. 冻结基础网络
  3. 训练增加的网络部分
  4. 解冻基础网络中的一些层
  5. 一起训练这些解冻层和增加层

在进行特征提取时,我们已经完成了前三个步骤。让我们继续第4步:我们将解冻我们的conv_base,然后冻结它内部的各个层。

>>> conv_base.summary()
Layer (type) Output Shape Param #
================================================================
input_1 (InputLayer) (None, 150, 150, 3) 0
________________________________________________________________
block1_conv1 (Convolution2D) (None, 150, 150, 64) 1792
________________________________________________________________
block1_conv2 (Convolution2D) (None, 150, 150, 64) 36928
________________________________________________________________
block1_pool (MaxPooling2D) (None, 75, 75, 64) 0
________________________________________________________________
block2_conv1 (Convolution2D) (None, 75, 75, 128) 73856
________________________________________________________________
block2_conv2 (Convolution2D) (None, 75, 75, 128) 147584
________________________________________________________________
block2_pool (MaxPooling2D) (None, 37, 37, 128) 0
________________________________________________________________
block3_conv1 (Convolution2D) (None, 37, 37, 256) 295168
________________________________________________________________
block3_conv2 (Convolution2D) (None, 37, 37, 256) 590080
________________________________________________________________
block3_conv3 (Convolution2D) (None, 37, 37, 256) 590080
________________________________________________________________
block3_pool (MaxPooling2D) (None, 18, 18, 256) 0
________________________________________________________________
block4_conv1 (Convolution2D) (None, 18, 18, 512) 1180160
________________________________________________________________
block4_conv2 (Convolution2D) (None, 18, 18, 512) 2359808
________________________________________________________________
block4_conv3 (Convolution2D) (None, 18, 18, 512) 2359808
________________________________________________________________
block4_pool (MaxPooling2D) (None, 9, 9, 512) 0
________________________________________________________________
block5_conv1 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_conv2 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_conv3 (Convolution2D) (None, 9, 9, 512) 2359808
________________________________________________________________
block5_pool (MaxPooling2D) (None, 4, 4, 512) 0
================================================================
Total params: 14714688

我们将对最后的3个卷积层进行 Fine - tuning,这意味着直到block4_pool应该被冻结,并且层block5_conv1, block5_conv2和block5_conv3应该是可训练的。

为什么不调整更多的层次呢?为什么不微调整个卷积基础?我们可以。然而,我们需要考虑:

  1. 在卷积基础上,较早的层可以编码更通用的、可重用的特性,而层次较高的则可以编码更专门化的特性。对更专门化的特性进行微调更有用,因为这些特性需要对我们的新问题进行重新规划。
  2. 我们训练的参数越多,就越有可能被过度拟合。卷积基有15M个参数,所以尝试在我们的小数据集上训练它是有风险的。

因此,在我们的情况下,在卷积基础上只调整前2到3层是一个很好的策略。

conv_base.trainable = Trueset_trainable = False
for layer in conv_base.layers:if layer.name == 'block5_conv1':set_trainable = Trueif set_trainable:layer.trainable = Trueelse:layer.trainable = False

现在我们可以开始微调我们的网络了。我们将使用一个非常低的学习速率来使用RMSprop优化器。使用低学习率的原因是,我们想要限制我们对三层的表示的修改的大小,我们正在进行微调。太大的更新可能会损害这些表示。

model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-5),metrics=['acc'])history = model.fit_generator(train_generator,steps_per_epoch=100,epochs=100,validation_data=validation_generator,validation_steps=50)

这里写图片描述

这些曲线看起来有很多Noise。为了使它们更具可读性,我们可以通过用指数移动的平均数来替换每一个损失和精度来使它们平滑。这里是一个平凡的效用函数。

Smoothing our plots:

def smooth_curve(points, factor=0.8):smoothed_points = []for point in points:if smoothed_points:previous = smoothed_points[-1]smoothed_points.append(previous * factor + point * (1 - factor))else:smoothed_points.append(point)return smoothed_pointsplt.plot(epochs,smooth_curve(acc), 'bo', label='Smoothed training acc')
plt.plot(epochs,smooth_curve(val_acc), 'b', label='Smoothed validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()plt.plot(epochs,smooth_curve(loss), 'bo', label='Smoothed training loss')
plt.plot(epochs,smooth_curve(val_loss), 'b', label='Smoothed validation loss')
plt.title('Training and validation loss')
plt.legend()plt.show()

这里写图片描述

test_generator = test_datagen.flow_from_directory(test_dir,target_size=(150, 150),batch_size=20,class_mode='binary')test_loss, test_acc = model.evaluate_generator(test_generator, steps=50)
print('test acc:', test_acc)

总结

这是你应该从之前的内容学到的:

  1. Convnets是计算机视觉任务的最好的机器学习模型。即使是在非常小的数据集上,也可以从头开始训练一个,结果很好。
  2. 在一个小数据集上,过拟合将是主要问题。在处理图像数据时,数据增强是一种有效的方法。
  3. 通过特征提取,很容易在新的数据集上重用现有的convnet。这是一个非常有价值的技术,用于处理小的图像数据集。
  4. 作为特征提取的补充,可以使用微调,这将适应一个新问题,其中一些是由现有模型学习的。这进一步推动了性能。
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Hibernate一级缓存和二级缓存的区别与联系

    Hibernate一级缓存和二级缓存的区别与联系来源:http://blog.csdn.net/wangmj518/archive/2008/12/22/3582775.aspx缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制…...

    2024/4/17 8:01:28
  2. 15个热门的编程趋势及15个逐步走向衰落的编程方向(上)

    本文来源于我在InfoQ中文站原创的文章,原文地址是:http://www.infoq.com/cn/news/2014/02/15-hot-programming-trendsPeter Wayner是InfoWorld的一名特约编辑,也是一个多产的作家。除了InfoWorld之外,他还经常为纽约时报和连线杂志撰写文章。近日,Peter撰写了一篇文章,谈…...

    2024/4/17 8:01:28
  3. 推荐一本最近很火的机器学习必备数学基础 电子书

    点击上方“Python与算法社区”,选择“星标”公众号编辑:zglgOPENNING1 机器学习中数学怎么学?做公众号期间,我和很多老铁探讨过,要想真正做到研究机器学习的水平,的确需要一定的数学基础。那么我们是不是先去修个数学系的学位,然后才能踏实地做机器学习研究呢,很显然不…...

    2024/4/24 8:57:33
  4. 【TensorFlow学习笔记(二)】常用方法:激活函数

    更新时间:2019-06-07激活函数 tf.nn.relu() ReLU函数,修正线性单元,在卷积神经网络中应用广泛,定义如下: (1)ReLU(x)=max(0,x)ReLU(x)=max(0,x) \tag{1}ReLU(x)=max(0,x)(1) ReLU函数具有如下性质: 优点: (1)计算高效:采用ReLU函数的神经元只需要进行加、乘和比较的操…...

    2024/4/17 8:01:46
  5. hibernate笔记04 hibernate一级缓存概述

    ---Hibernate缓存 1.Hibernate框架中提供很多优化方式,hibernate的缓存就是一个优化方式2.hibernate缓存特点:第一类 hibernate的一级缓存(1)hibernate的一级缓存默认打开(2)hibernate的一级缓存使用范围是session范围,从session创建到session关闭范围(3)hibernate的…...

    2024/4/17 8:01:46
  6. 《变革中的思索》连载二:依旧有效的四律

    2. 依旧有效的四律 IT从业者都知道著名的“摩尔定律”(Moores Law)和“贝尔定律”(Bells Law),这两大定律互相补充,前者断定微处理器的速度会每18个月翻一倍,而后者则认为如果保持计算能力不变,微处理器的价格和体积每18个月减少一半,这就意味着同等价位的微处理器的速…...

    2024/4/17 8:01:52
  7. 蓝以中老师《高等代数》第04章引言:线性空间与线性变换

    本篇博客,直接把蓝老师中的教材中的两页,给复制粘贴了出来。而没有任何其他的说明。也没有任何其他的内容。这么做,就是想突出这两页的重要性。这两页,可以说总结了代数的最本质的东西。甚至,可以毫不客气地讲:代数这门学科,之所以叫做“代数”,代数之所以先进;更进一…...

    2024/4/17 21:28:42
  8. Hibernate的缓存_一级缓存

    什么是缓存 数据存到数据库里面,数据库本身是文件系统,使用流方式操作文件效率不是很高。把数据库存放到内存里面,不需要流方式,可以直接读取内存中的数据,提高了读取效率。 Hibernate缓存 hibernate缓存是一种优化方式。 1、hibernate缓存的一级缓存: ①hibernate一级缓…...

    2024/4/17 8:03:40
  9. 小甲鱼Python教程第21讲--python函数:lambda表达式习题及答案

    1.匿名函数与lambda表达式def ds(x):return 2*x+1lambda x:2*x+1 #lambda表达式 g=lambda x:2*x+1 g(5) 使用lambda表达式lambda表达式的作用 1)lambda可以省下定义函数的过程,不用专门定义一个函数然后在写调用,使用lambda就可以使代码更加精简 2)有些只需要调用一两次…...

    2024/4/17 8:02:52
  10. managedQuery()与getContentResolver().query()的区别

    managedQuery()与getContentResolver().query()的区别:Romain Guy写道:[url]http://groups.google.com/group/android-developers/browse_thread/thread/9c887ac024b36798[/url]managedQuery() will use ContentResolvers query(). The difference is that with managedQuery…...

    2024/4/17 8:02:46
  11. 使用Airbnb的Airflow来管理数据工作流

    Airbnb 最近在Apache许可证下开源了它自己的数据工作流管理框架 Airflow 。Airflow被Airbnb内部用来创建、监控和调整数据管道。任何工作流都可以在这个使用Python来编写的平台上运行。 Airflow是一种允许工作流开发人员轻松创建、维护和周期性地调度运行工作流(即有向无环图…...

    2024/4/18 1:55:50
  12. 3D数学基础:图形与游戏开发---随笔五

    向量运算5.1 线性代数与几何数学中专门研究向量的分支称作线性代数。5.2 符号约定变量是代表未知量的占位符。本书用不同的字体来区分不同的变量:标量,用斜体的小谢罗马或者希腊字母表示,如a、b、θ。 向量,用小写黑粗体字母表示,如a、b、u。 矩阵,用大写黑粗体字母表示,…...

    2024/4/4 22:41:34
  13. 【深度学习】深度学习中常用的激活函数

    深度学习中常用的的激活函数包括了:sigmoid、tanh、ReLU 、Leaky Relu、RReLU、softsign 、softplus。激活函数可以分为两大类 :饱和激活函数: sigmoid、 tanh非饱和激活函数: ReLU 、Leaky Relu 、ELU【指数线性单元】、PReLU【参数化的ReLU 】、RReLU【随机ReLU】相对于…...

    2024/4/26 0:27:03
  14. hibernate一级缓存详解

    一级缓存的生命周期和session的生命周期一致,当前sessioin一旦关闭,一级缓存就消失,session间不能共享一级缓存的数据,因此一级缓存也叫session级的缓存或事务级缓存。一级缓存只存实体对象的 ,它不会缓存一般的对象属性(查询缓存可以),即当获得对象后,就将该对象的缓…...

    2024/3/31 20:23:08
  15. 一个真正符合中国国情的工作流设计参考(包括PHP实现)

    开源的工作流很少有让人满意的,即便是国内用的比较多的jbpm,用起来也会觉得很便扭。再加上PHP中没有什么好用的工作流,于是干脆自己设计一个,设计的原则如下:1 根据80/20原则,只使用wfmc模型中最符合自身应用的20%功能2 充分吸收国内使用jbpm开发BOSS中遇到的问题,工作…...

    2024/4/19 20:52:06
  16. hibernate二级缓存攻略 Ehcache(from javaEyes)

    hibernate二级缓存攻略 Ehcache(from javaEyes) 很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了。 我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化。 hibernate…...

    2024/4/20 1:21:54
  17. 2011优秀创意和设计

    《2011室内优秀创意设计》东田造型无锡店-著名建筑师刘昊威作品 进入东田造型无锡店,仿佛置身于一个白色的梦境中:空间吊顶由各种不规则的圆形构成,它们变形并向墙面延伸,建筑师刘威,整个空间没有一处棱角,是弧线与曲面的结合,著名建筑师刘威,温暖柔和的光线溢满空隙,如…...

    2024/4/17 8:02:46
  18. 李代数(第2版) 万哲先 著

    作者: 万哲先 著 出版社: 高等教育出版社 出版时间: 2013-06 版次: 1 ISBN: 9787040372663 装帧: 平装 开本: 16开 纸张: 胶版纸 页数: 278页 字数: 340千字 正文语种: 简体中文 丛书: 现代数学基础 分类: 自然科学 内容简介:《现代数学基础:李代数(第2版)》作者在中国科学院…...

    2024/4/17 8:03:46
  19. 激活函数

    ELU激活函数https://blog.csdn.net/zrh_CSDN/article/details/81266188 融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性。右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。ELU的输出均值接近于零,所以收敛速度更快。在 ImageNet上,…...

    2024/4/17 8:02:40
  20. Pytorch 学习(7):Pytorch中的Non-linear Activations (非线性层)实现

    Pytorch 学习(7):Pytorch中的Non-linear Activations (非线性层)实现Pytorch中的Non-linear Activations (非线性层)包括以下激活函数:ReLUReLU6ELUSELUPReLULeakyReLUThresholdHardtanhSigmoidTanhLogSigmoidSoftplusSoftshrinkSoftsignTanhshrinkSoftminSoftmaxSoftmax2d…...

    2024/4/17 8:02:40

最新文章

  1. 快速了解Oracle 数据库 23ai

    Oracle Database 23ai 于2024年5月2日正式发布。快速了解可以看官网主页和官方博客。 官网主页 23ai的3个要点,核心都是数据: 数据的人工智能 主要指数据库内置机器学习和AI向量搜索。这实际是Oracle融合数据库策略的延续,避免了复杂的数据…...

    2024/5/6 21:48:52
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. [C++][算法基础]模拟队列(数组)

    实现一个队列,队列初始为空,支持四种操作: push x – 向队尾插入一个数 x;pop – 从队头弹出一个数;empty – 判断队列是否为空;query – 查询队头元素。 现在要对队列进行 M 个操作,其中的每…...

    2024/5/5 0:14:29
  4. llama.cpp运行qwen0.5B

    编译llama.cp 参考 下载模型 05b模型下载 转化模型 创建虚拟环境 conda create --prefixD:\miniconda3\envs\llamacpp python3.10 conda activate D:\miniconda3\envs\llamacpp安装所需要的包 cd G:\Cpp\llama.cpp-master pip install -r requirements.txt python conver…...

    2024/5/5 7:48:22
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57