在Kaggle平台上找到Digit Recognizer项目,下载数据集,建立模型。
这里使用的是ResNet:深度残差网络(Residual Neural Network,简称 ResNet
)算法。
残差:观测值与估计值之间的差
为什么使用ResNet:网络训练到较深层时,会出现退化问题(继续训练,损失反而增加),使用ResNet就是为了解决这个问题。
ResNet原理:
ResNet通过在卷积层的输入和输出之间添加Skip Connection实现层数回退机制,如下图所示:
输入𝒙通过两个卷积层,得到特征变换后的输出ℱ(𝒙),与输入x进行对应元素的相加运算,得到最终输出ℋ(𝒙):
ℋ(𝒙) = 𝒙 + ℱ(𝒙)
H(x)叫做残差模块(Residual Block, 简称 ResBlock)。由于被 Skip Connection 包围的卷积神
经网络需要学习映射ℱ(𝒙) = ℋ(𝒙) - 𝒙,故称为残差网络。
ResBlock实现:
深度残差网络并没有增加新的网络层类型,只是通过在输入和输出之间添加一条 Skip
Connection, 因此并没有针对 ResNet 的底层实现。 在 TensorFlow 中通过调用普通卷积层即
可实现残差模块。
完整代码:
#引入所需要的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import keras
import sklearn.model_selection
from sklearn.preprocessing import scale
import tensorflow as tf
from tensorflow.keras import Sequential,layers
#导入数据集
data=pd.read_csv('./train.csv')
test_data=pd.read_csv('./test.csv')
data.head()
#建立最优模型参数文件,用来存储训练完的模型参数
train = True
model_file = "top_model_resnet_1.h5"
#数据处理
label=["label"]#找出标签
Y=data[label].values#Y为标签
X=data.iloc[:,1:].values#去掉第一列,剩下的为训练数据
X=scale(X)#沿任意轴标准化数据集,每一列都标准化
X=X.reshape(len(X),28,28).astype('float32')
X=np.expand_dims(X,axis=-1)#X末尾增加一维
Y=np.expand_dims(Y,axis=-1)#Y末尾增加一维
X_train=X
Y_train=Y
#数据扩充
inputs=keras.Input(shape=(28,28,1))#初始化深度学习网络输入层的tensor。输入层
x=tf.keras.layers.experimental.preprocessing.RandomTranslation(height_factor=0.15, width_factor=0.15)(inputs)#翻译图像
x=tf.keras.layers.experimental.preprocessing.RandomRotation(factor=0.20)(x)#随机旋转图像
#定义renet
def res_net_block(input_data,filters,conv_size):
x=layers.Conv2D(filters,conv_size,activation='relu',padding='same')(input_data)
x=layers.BatchNormalization()(x)#批归一化
x=layers.Conv2D(filters,conv_size,activation=None,padding='same')(x)
x=layers.BatchNormalization()(x)
x=layers.Add()([x,input_data])#计算输入张量列表的和。它接受一个张量的列表(可以多个一起相加),所有的张量必须有相同的输入尺寸,然后返回一个张量(和输入张量尺寸相同)
x=layers.Activation('relu')(x)
return x
#建立model
x=keras.layers.Conv2D(filters=28,kernel_size=3,input_shape=(28,28,1),padding='same')(inputs)
num_res_net_blocks_1=25
num_res_net_blocks_2=25
for i in range(num_res_net_blocks_1):
x=res_net_block(x,28,5)
x=layers.Dropout(0.3)(x)
for i in range(num_res_net_blocks_2):
x=res_net_block(x,28,5)
x=layers.Conv2D(14,3,activation='relu')(x)
x=layers.GlobalAveragePooling2D()(x)#全局平均池化层
x=keras.layers.Flatten()(x)#打平
x=keras.layers.Dense(256,activation='relu')(x)
x=keras.layers.Dense(64,activation='relu')(x)
outputs=layers.Dense(10,activation='softmax')(x)
model=keras.Model(inputs,outputs)
#训练
model.compile(optimizer='adam',metrics=['accuracy'],loss='sparse_categorical_crossentropy')
history=model.fit(np.array(X_train),Y_train,epochs=80)
model.save(model_file)
print("Model saved!")
#调用存储的模型参数文件
model: keras.Model
model = keras.models.load_model(model_file)
test_data = pd.read_csv("./testA.csv")
X = test_data.values
X = scale(X)#标准化
X = X.reshape(len(X), 28, 28).astype('float32')
X = np.expand_dims(X, axis=-1)
#预测
y = model.predict(X)
with open("submission.csv", "w") as of:
of.write("imageid,label\n")
for i in range(len(y)):
of.write(str(i+1) + "," + str(np.argmax(y[i])) + "\n")
print("Done!")
训练过程:
kaggle 分数:
代码参考 kaggle平台@grzegorz