由于博主之前已经写了一篇mnist的文章,关于mnist数据集的介绍这里就不过多赘述了,简单来说mnist中有6万张训练图片,1万张测试图片,每张图片都是手写数字0-9中一个。我们训练模型希望模型能正确做出预测。
本文的重点在于用卷积层代替最后的全连接层。
一般的图片分类,最后一般是使用Flatten层和Dense层。
以博主之前那篇文章为例,模型的结构是:
如何用卷积层代替Flatten层和Dense层呢?
首先我们得明确,最后一层一定有十个输出,用于做分类交叉熵。
此时,便想到了用1*1卷积,将通道数设为10,然后通过一个全局平均池化得到每个通道所有像素的平均值,然后再通过Reshape层将输出变成一维张量,最后通过Softmax层,这样便代替了展平和全连接层。
下面来实际试验一下
下面代码就不详细解释了,有疑问的可以看我这篇文章用二维卷积做mnist手写数字识别
细节部分只有定义模型最后几层有区别
from keras import layers
import numpy as np
from keras.models import Model
from keras.losses import CategoricalCrossentropy
from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
from keras.models import Sequential
from keras.utils import Sequence
import math
(x_train, y_train), (x_test, y_test) = mnist.load_data()
y_test = to_categorical(y_test)
y_train = to_categorical(y_train)
class Generator(Sequence):
def __init__(self, x, y, b_size):
self.x, self.y = x, y
self.batch_size = b_size
def __len__(self):
return math.ceil(len(self.y)/self.batch_size)
def __getitem__(self, idx):
batch_x = self.x[idx * self.batch_size:(idx + 1) *
self.batch_size]
batch_y = self.y[idx * self.batch_size:(idx + 1) *
self.batch_size]
batch_x = batch_x/255.0
batch_x = batch_x.reshape(*batch_x.shape,1)
return batch_x,batch_y
def on_epoch_end(self):
pass
from keras.callbacks import EarlyStopping,ModelCheckpoint
callbacks_list = [
ModelCheckpoint(
filepath = 'my_test_conv2d.h5', #模型保存路径
monitor = 'val_acc', #
save_best_only = True, #如果val_loss没有改善则不需要覆盖模型
)
]
定义模型
#模型定义
model=Sequential([
layers.Conv2D(64,(3,3),padding='same',activation='relu',input_shape=(28,28,1)),
layers.MaxPool2D(2,2),
layers.Conv2D(128,(3,3),padding='same',activation='relu'),
layers.MaxPool2D(2,2),
layers.Conv2D(64,(3,3),activation='relu'),
layers.Conv2D(32,(3,3),padding='same',activation='relu'),
layers.Conv2D(10,(1,1)),
layers.GlobalAveragePooling2D(),
layers.Reshape((10,)),
layers.Softmax(),
])
model.compile(loss=CategoricalCrossentropy(),optimizer="adam",metrics=["acc"])
查看模型结构
model.summary()
训练模型
history = model.fit_generator(Generator(x_train,y_train,200),epochs=6,validation_data=Generator(x_test,y_test,200),callbacks=callbacks_list)
训练结果如下:
Epoch 1/6
300/300 [==============================] - 262s 874ms/step - loss: 0.6324 - acc: 0.7965 - val_loss: 0.2068 - val_acc: 0.9436
Epoch 2/6
300/300 [==============================] - 264s 880ms/step - loss: 0.1777 - acc: 0.9481 - val_loss: 0.1442 - val_acc: 0.9479
Epoch 3/6
300/300 [==============================] - 263s 877ms/step - loss: 0.1193 - acc: 0.9645 - val_loss: 0.1141 - val_acc: 0.9654
Epoch 4/6
300/300 [==============================] - 264s 880ms/step - loss: 0.0920 - acc: 0.9719 - val_loss: 0.0995 - val_acc: 0.9735
Epoch 5/6
300/300 [==============================] - 263s 878ms/step - loss: 0.0748 - acc: 0.9769 - val_loss: 0.0914 - val_acc: 0.9775
Epoch 6/6
300/300 [==============================] - 268s 893ms/step - loss: 0.0639 - acc: 0.9802 - val_loss: 0.0265 - val_acc: 0.9841
第一轮训练的效果不太理想,训练精度只达到了0.79,但是似乎还有着上升空间,在接下来的几轮中,模型精度不断提高,六轮过后验证精度达到了0.984,但是它似乎还有进步的空间。于是再训练四轮。
model.fit_generator(Generator(x_train,y_train,200),epochs=4,validation_data=Generator(x_test,y_test,200),callbacks=callbacks_list)
训练结果如下
Epoch 1/4
300/300 [==============================] - 272s 908ms/step - loss: 0.0362 - acc: 0.9884 - val_loss: 0.0232 - val_acc: 0.9887
Epoch 2/4
300/300 [==============================] - 268s 893ms/step - loss: 0.0313 - acc: 0.9900 - val_loss: 0.0093 - val_acc: 0.9908
Epoch 3/4
300/300 [==============================] - 270s 901ms/step - loss: 0.0292 - acc: 0.9907 - val_loss: 0.0184 - val_acc: 0.9878
Epoch 4/4
300/300 [==============================] - 265s 884ms/step - loss: 0.0285 - acc: 0.9911 - val_loss: 0.0032 - val_acc: 0.9913
模型最终还是达到了0.991的验证精度,和模型最后用全连接层达到的效果差不多。
以上只是博主突发奇想的一次尝试,虽然最后也能达到较高的精度,但是相比于最后用全连接层的训练速度更慢,这个模型能否在别的问题上发挥出更好的效果还有待尝试。
今天的分享就到这里了,感谢大家的阅读,欢迎关注,一起进步~