深度学习优化器与全卷积网络在MNIST数据集上的实践
1. 优化器性能比较
在深度学习中,不同的优化器在性能上存在一定差异,但总体结果差异并不是特别大。通过观察误差线可以发现,Adadelta、Adagrad和Adam的表现略优于SGD或RMSprop,这在深度学习文献中也得到了证实。不过,每个数据集都需要单独进行评估。
在训练时间方面,不同优化器大致相当,但SGD始终是最快的,这种性能差异在处理非常大的数据集时可能会很重要。此外,Adam在性能基本相同的情况下,也始终比Adadelta更快。SGD和Adam在实际应用中被广泛使用。
| 优化器 | 性能表现 | 训练时间 |
|---|---|---|
| Adadelta | 略优 | 适中 |
| Adagrad | 略优 | 适中 |
| Adam | 略优 | 较快 |
| SGD | 稍逊 | 最快 |
| RMSprop | 稍逊 | 适中 |
2. 全卷积网络的构建与训练
为了让模型能够在任意大小的图像中定位目标,我们可以将基本的MNIST CNN模型转换为全卷积版本。具体步骤如下:
1.
训练基础模型
:在完整的MNIST数据集上训练基础模型,将训练轮数从12轮增加到24轮,训练完成后会得到一个包含训练好的权重和偏置的HDF5文件。
2.
创建全卷积版本
:通过更改全连接层来创建全卷积版本的模型,并将旧模型的权重和偏置复制到新模型中。
以下是创建训练好的全连接模型的代码:
from keras.models import Sequential, load_model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
weights = load_model('mnist_cnn_base_model.h5').get_weights()
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=(None,None,1)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(128, (12,12), activation='relu'))
model.add(Dropout(0.5))
model.add(Conv2D(10, (1,1), activation='softmax'))
model.layers[0].set_weights([weights[0], weights[1]])
model.layers[1].set_weights([weights[2], weights[3]])
model.layers[4].set_weights([weights[4].reshape([12,12,64,128]), weights[5]])
model.layers[6].set_weights([weights[6].reshape([1,1,128,10]), weights[7]])
model.save('mnist_cnn_fcn_model.h5')
在构建全卷积版本的模型时,有几个关键的区别:
-
输入卷积层
:在全连接模型中,我们指定了输入图像的大小为28×28像素,单通道(灰度图)。而在全卷积模型中,由于不知道输入的大小,我们将宽度和高度设置为None,但仍然知道输入是单通道图像,所以保留1。
-
替换全连接层
:由于全连接层需要固定大小的输入,我们将其替换为等效的卷积层。例如,将
model.add(Flatten())
和
model.add(Dense(128, activation='relu'))
替换为
model.add(Conv2D(128, (12,12), activation='relu'))
。
-
最终的softmax层
:同样需要将其转换为全卷积层,输出有10个,每个数字对应一个,激活函数保持不变,内核大小为1×1。
3. 测试图像的制作
为了测试全卷积模型,我们需要创建包含多个数字的大尺寸测试图像。这些图像与训练图像不同,训练图像较小且数字位于中心,而测试图像要包含任意位置的多个数字。由于MNIST数据集是黑色背景上的灰度图,所以测试图像也应该具有黑色背景,以确保测试图像与训练图像来自相同的“领域”。
以下是构建大型MNIST测试集图像的代码:
import os
import sys
import numpy as np
import random
from PIL import Image
os.system("rm -rf images; mkdir images")
if (len(sys.argv) > 1):
N = int(sys.argv[1])
else:
N = 10
x_test = np.load("data/mnist/mnist_test_images.npy")
for i in range(N):
r,c = random.randint(6,12), random.randint(6,12)
g = np.zeros(r*c)
for j in range(r*c):
if (random.random() < 0.15):
g[j] = 1
g = g.reshape((r,c))
g[:,0] = g[0,:] = g[:,-1] = g[-1,:] = 0
img = np.zeros((28*r,28*c), dtype="uint8")
for x in range(r):
for y in range(c):
if (g[x,y] == 1):
n = random.randint(0, x_test.shape[0])
im = x_test[n]
img[28*x:(28*x+28), 28*y:(28*y+28)] = im
Image.fromarray(img).save("images/image_%04d.png" % i)
制作测试图像的具体步骤如下:
1.
确定图像大小
:随机确定大图像的行数和列数,以28×28的MNIST数字为单位。
2.
创建网格
:创建一个网格,每个可能的数字位置有0或1,有15%的概率该位置为1,然后将网格重塑为二维数组,并将边界位置设置为0,以确保数字不会出现在图像边缘。
3.
填充图像
:根据网格的值,随机选择数字并将其复制到输出图像的相应位置。
4. 模型的测试
我们将对模型进行两次测试,首先在单个MNIST数字上进行测试,然后在随机生成的大数字图像上进行测试。
测试单个MNIST数字 :
import numpy as np
from keras.models import load_model
x_test = np.load("data/mnist/mnist_test_images.npy")/255.0
y_test = np.load("data/mnist/mnist_test_labels.npy")
model = load_model("mnist_cnn_fcn_model.h5")
N = y_test.shape[0]
nc = nw = 0.0
for i in range(N):
p = model.predict(x_test[i][np.newaxis,:,:,np.newaxis])
c = np.argmax(p)
if (c == y_test[i]):
nc += 1
else:
nw += 1
print("Single MNIST digits, n=%d, accuracy = %0.2f%%" % (N, 100*nc/N))
在测试单个MNIST数字时,我们加载MNIST测试图像和标签,以及全卷积模型,然后对每个测试图像进行预测。由于图像是二维的,但
predict
方法需要四维数组,所以使用
np.newaxis
创建缺失的轴。预测结果存储在
p
中,通过
np.argmax
获取最大概率对应的标签。如果预测标签与实际测试标签匹配,则增加正确预测的数量;否则,增加错误预测的数量。最终输出整体准确率,在我的训练中,全卷积模型的准确率达到了99.25%。
测试大MNIST数字图像 :
import os
import numpy as np
from keras.models import load_model
from PIL import Image
model = load_model("mnist_cnn_fcn_model.h5")
os.system("rm -rf results; mkdir results")
n = len(os.listdir("images"))
for i in range(n):
f = "images/image_%04d.png" % i
im = np.array(Image.open(f))/255.0
p = model.predict(im[np.newaxis,:,:,np.newaxis])
np.save("results/results_%04d.npy" % i, p[0,:,:,:])
在测试大MNIST数字图像时,我们加载全卷积模型,创建一个新的输出目录,然后对每个大数字图像进行预测。同样,需要将图像转换为四维数组,由于模型是全卷积的,即使输入图像大于28×28且包含多个数字,也不会出现错误。预测结果
p
是一个四维数组,第一个维度为1,表示输入图像的数量,最后一个维度为10,表示数字的数量,中间两个维度取决于输入图像的大小。
5. 热力图的生成
预测结果可以看作是概率图,通常称为热力图,它给出了在特定位置存在数字的概率。为了更直观地观察模型的响应,我们可以生成热力图。
以下是构建热力图图像的代码:
import os
import sys
import numpy as np
from PIL import Image
threshold = float(sys.argv[1])
iname = sys.argv[2]
rname = sys.argv[3]
outdir = sys.argv[4]
os.system("rm -rf %s; mkdir %s" % (outdir, outdir))
img = Image.open(iname)
c,r = img.size
hmap = np.zeros((r,c,10))
res = np.load(rname)
x,y,_ = res.shape
xoff = (r - 2*x) // 2
yoff = (c - 2*y) // 2
for j in range(10):
h = np.array(Image.fromarray(res[:,:,j]).resize((2*y,2*x)))
hmap[xoff:(xoff+x*2), yoff:(yoff+y*2),j] = h
np.save("%s/graymaps.npy" % outdir, hmap)
hmap[np.where(hmap < threshold)] = 0.0
for j in range(10):
img = np.zeros((r,c), dtype="uint8")
for x in range(r):
for y in range(c):
img[x,y] = int(255.0*hmap[x,y,j])
img = 255-img
Image.fromarray(img).save("%s/graymap_digit_%d.png" % (outdir, j))
生成热力图的步骤如下:
1.
输入参数
:传入阈值、源图像名称、模型对该源图像的响应以及输出目录。
2.
加载图像和响应
:加载源图像以获取其尺寸,创建输出热力图
hmap
,并加载模型响应
res
,计算偏移量。
3.
填充热力图
:将调整大小后的模型响应填充到
hmap
的每个数字灰度图中,并将完整的灰度图集存储在输出目录中。
4.
阈值处理
:对热力图进行阈值处理,将小于阈值的值设置为0。
5.
生成灰度图像
:为每个数字创建输出图像,将剩余的热力图值乘以255进行缩放,然后通过减去255进行反转,最后将图像保存到磁盘。
6. 模型响应分析与改进
通过观察热力图,我们发现全卷积模型的响应存在噪声。例如,输入图像中没有2,但模型却有很多强响应;对于某些数字,如8,模型表现良好,但对于其他数字,如7,则表现较差。
这是因为我们在标准MNIST数字数据集上训练模型,数据集中的所有数字都位于图像中心。当模型在大输入图像上进行卷积时,输入可能只是部分数字,而模型从未见过部分数字,因此有时会给出无意义的答案。
为了解决这个问题,我们可以通过增加标准MNIST数据集的偏移版本来教模型识别部分MNIST数字。以下是随机偏移函数的代码:
import random
import numpy as np
def shifted(im):
r,c = im.shape
x = random.randint(-r//4, r//4)
y = random.randint(-c//4, c//4)
img = np.zeros((2*r,2*c), dtype="uint8")
xoff = r//2 + x
yoff = c//2 + y
img[xoff:(xoff+r), yoff:(yoff+c)] = im
img = img[r//2:(r//2+r),c//2:(c//2+c)]
return img
使用增强数据集的步骤如下:
1.
重新训练全连接MNIST模型
:使用增强后的数据集重新训练全连接MNIST模型。
2.
重建全卷积模型
:使用新的权重和偏置重建全卷积模型。
3.
再次测试
:像之前一样,将大测试图像通过模型进行测试,得到新的灰度图。
通过以上步骤,我们可以提高模型在处理大图像时的性能,使其能够更准确地定位数字。
graph LR
A[训练基础模型] --> B[创建全卷积版本]
B --> C[制作测试图像]
C --> D[测试模型]
D --> E[生成热力图]
E --> F[分析响应并改进]
综上所述,通过将MNIST CNN模型转换为全卷积版本,并对模型进行测试和优化,我们可以让模型在任意大小的图像中定位数字。同时,通过增加数据集的多样性,可以提高模型的性能和鲁棒性。
深度学习优化器与全卷积网络在MNIST数据集上的实践
7. 增强数据集后的效果分析
使用增强后的数据集重新训练模型并进行测试后,我们可以看到模型性能有了一定的提升。之前模型在处理部分数字时出现的误判情况有所减少,例如那些原本没有该数字但模型却有很多强响应的情况得到了改善。
以下是对比表格,展示了增强数据集前后模型对不同数字的识别情况:
| 数字 | 增强前表现 | 增强后表现 |
| ---- | ---- | ---- |
| 2 | 输入无2但有很多强响应 | 强响应减少 |
| 7 | 表现较差 | 部分情况改善 |
| 8 | 表现良好 | 维持较好表现 |
| 5 | 无5但有很多命中 | 命中情况减少 |
然而,模型仍然存在一些问题。虽然大部分误判情况得到缓解,但对于一些复杂的图像区域,模型还是会出现误判。这可能是因为即使增加了偏移版本的数字,模型对于一些极端的部分数字情况仍然缺乏足够的学习。
8. 优化器选择与模型性能的进一步探讨
在前面我们对比了不同优化器的性能,SGD速度最快,而Adadelta、Adagrad和Adam表现略优。在实际应用中,我们可以根据具体的需求来选择优化器。
如果数据集非常大,训练时间是一个关键因素,那么SGD可能是一个不错的选择。例如,当处理大规模的图像数据集时,SGD的快速训练特性可以节省大量的时间。
如果更注重模型的准确性,那么Adadelta、Adagrad或Adam可能更合适。这些优化器在处理复杂的数据集时,能够更好地调整模型的参数,从而提高模型的性能。
以下是不同优化器在不同场景下的选择建议表格:
| 场景 | 推荐优化器 | 原因 |
| ---- | ---- | ---- |
| 大数据集,追求速度 | SGD | 训练速度快 |
| 复杂数据集,追求准确性 | Adadelta、Adagrad、Adam | 能更好调整参数,提高性能 |
9. 全卷积网络的优势与局限性
全卷积网络的主要优势在于它能够处理任意大小的输入图像。在传统的全连接网络中,输入图像的大小必须是固定的,这限制了网络的应用范围。而全卷积网络通过将全连接层替换为卷积层,解决了这个问题,使得网络可以在不同大小的图像上进行卷积操作。
然而,全卷积网络也存在一些局限性。例如,在处理部分数字的情况时,由于模型训练时缺乏对部分数字的学习,导致出现误判。此外,全卷积网络的计算量相对较大,尤其是在处理大尺寸图像时,需要更多的计算资源和时间。
10. 未来改进方向
为了进一步提高模型的性能,我们可以考虑以下几个改进方向:
1.
增加更多的数据增强方式
:除了偏移数字,还可以尝试旋转、缩放、裁剪等数据增强方式,让模型学习到更多不同形态的数字,从而提高模型对部分数字和复杂图像的处理能力。
2.
调整模型结构
:可以尝试不同的卷积层结构和参数,例如增加卷积层的数量、调整卷积核的大小等,以提高模型的特征提取能力。
3.
结合其他技术
:可以将全卷积网络与其他技术,如注意力机制、生成对抗网络等结合,进一步提高模型的性能和鲁棒性。
11. 总结
本文详细介绍了深度学习优化器在MNIST数据集上的性能比较,以及如何将MNIST CNN模型转换为全卷积版本,并对模型进行测试和优化。通过对比不同优化器的性能,我们可以根据具体需求选择合适的优化器。全卷积网络的应用使得模型能够处理任意大小的图像,但也存在一些局限性,如对部分数字的处理能力不足。
通过增加数据集的多样性,如使用偏移版本的数字,我们可以在一定程度上提高模型的性能。未来,我们可以通过增加更多的数据增强方式、调整模型结构和结合其他技术等方法,进一步提高模型的性能和鲁棒性。
graph LR
A[选择优化器] --> B[构建全卷积网络]
B --> C[训练模型]
C --> D[测试模型]
D --> E{性能是否满足要求}
E -- 是 --> F[应用模型]
E -- 否 --> G[改进数据集或模型结构]
G --> C
总之,深度学习在图像识别领域有着广阔的应用前景,通过不断地优化模型和数据集,我们可以让模型在实际应用中发挥更好的作用。在实际操作中,我们需要根据具体的问题和需求,灵活选择和调整模型和方法,以达到最佳的效果。
超级会员免费看

被折叠的 条评论
为什么被折叠?



