1. 题目:
在本次比赛中,您的目标是从数以万计的手写图像数据集中正确识别数字。
1.1. Goal 目标✨
本次比赛的目标是拍摄手写个位数的图像,并确定该数字是什么。
对于测试集中的每个标签,您都应该预测正确的标签。
本次比赛的评估标准是预测的分类准确性(正确图像的百分比)。
1.2. Dataset Description 数据集描述
数据文件 train.csv 和 test.csv 包含手绘图数字的灰度图像,从 0 到 9。
每张图像高 28 像素,宽 28 像素,总共 784 像素。每个像素都有一个与之关联的像素值,表示该像素的明暗,数字越大表示越暗。此像素值是介于 0 和 255 之间的整数(含 0 和 255)。
2. 数据处理📊
2.1. python库📦
导入需要的库和包
# basic
import numpy as np
import pandas as pd# visuals
import matplotlib.pyplot as plt
import seaborn as sns# Scikit-learn
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix,ConfusionMatrixDisplay,classification_report# tensorflow
from keras.models import Sequential
from keras.layers import Conv2D,Dense,Flatten,MaxPooling2D,BatchNormalization,Dropout,SpatialDropout2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from keras.utils import to_categorical
from tensorflow.keras.callbacks import ReduceLROnPlateau,EarlyStopping
2.2. 观察数据
train_df = pd.read_csv(r'D:\WorkSpace\Kaggle\DigitRecognizer\data\InputData\train.csv')
test_df = pd.read_csv(r'D:\WorkSpace\Kaggle\DigitRecognizer\data\InputData\test.csv')
print(train_df.shape)
print(test_df.shape)
print(train_df.head())
print(test_df.head())
train数据是42000785,test是28000784,这还是我自参加kaggle以来接触过的最大的数据了,通过784个特征列去预测一个标签值。有点挑战难度呀🤣
👻像素点都是0和1,取决于像素是黑色的还是白色的,接着观察下去吧。
打印一下描述信息:
print(train_df.describe())
print(test_df.describe())
train数据的标签值的平均值是4.45,方差为2.884,方差不是特别大,最小值是0,最大值是9,其他特征列均为0,是因为分母太大啦,test数据也是一样的情况呀。
查看一下数据是否存在缺失值,盲猜是不存在缺失值的,因为都是图片呀,只有所有的像素点都存在才能构成原始图片,像素点还是要给全的吧。(虽然不排除图像损失需要根据已有的像素点去猜测原始图像,但这一题似乎并不考察这个点😎)
print(train_df.isnull().sum())
print(test_df.isnull().sum())
👻果然如我所料,无论是train还是test的每一列都确实不存在缺失值。
数据不需要处理呀,我们也基本了解train和test数据集啦。
3. 数据探索和数据分析EDA
3.1. 📊标签值分布
这个题是数字识别器,那train训练集中的数字是如何分布的呢,均匀还是不均匀,还是需要观察一下。
sns.countplot(data=train_df, x='label', palette='icefire').set_title('train set Label Distribution')
plt.show()
一共有0-9共十个数字,train训练集中的这十个数字分布还是比较平均的,让我们之后的预测会更加简单一些,至少可以在之后的学习和理解数字背后的模式出现不平衡。
太好啦,数字都是均匀分布(撒花)🎇🎇🎇
这个配色好好看,是“icefire”冰火色。
3.2. 数据可视化
3.2.1. 将数据调整为合适的图像🖼️
看看数据集里面的这些数字是什么样子的吧。
首先先将训练集和测试集的标签和特征分开,分别存储。然后将特征列数据的值重塑为适合卷积神经网络的形状。
在卷积神经网络中,输入数据通常是一个四维张量,其形状为[bath_size,height,width,channels]=[每个训练批次的样本数量,图像的高度,图形的宽度,图像的通道数],在处理图像数据时,需要将其重塑为这种形状,以便于在卷积神经网络中使用。
将train和test数据集的特征列都重塑为适合卷积神经网络的形状,[-1,28,28,1],然后除以255,这意味着每个样本都是一个28*28的灰度图像,其中每个像素的值都在0道1之间。
然后绘制了一个2*5的图像网格,绘制了在1-1000中随机选择的训练集图像,每个图像都是它的标签。这些图像可以帮助我们可视化数据集中的图像,以确保数据集被正确的加载和处理。
最后将所有子图都输出。
labels = train_df.label
features_train = train_df.drop(columns='label')
features_test = test_dffeatures_train = features_train.values.reshape(-1, 28, 28, 1)
features_test = features_test.values.reshape(-1, 28, 28, 1)features_train = features_train / 255
features_test = features_test / 255fig, axes = plt.subplots(2, 5, figsize=(10, 5))
for i in range(2):for j in range(5):idx = np.random.randint(1, 1000)ax = axes[i, j]ax.imshow(features_train[idx], cmap='gray')ax.set_title(f'Label: {labels[idx]}')ax.axis('off')
plt.show()
很显然手写数字并不像印刷体那样规范,比如“4”,都不像一个数字,再怎么说都像一个9。
在我随机选择的同时还存在许多非常不规范的图形,比如“7”、“9”,这两个手写形状还是比较难以辨认的,要不是看Label标签,我都不敢认这是“7”和“9”🤣
3.3. 数据增强
其实我的代码中没有比较这一步,但是我看到很多大佬都做了这一步,恰好我也不知道,就顺便学习一下。
数据增强是深度学习中的一种技术,可以从已有的数据中获取更多的数据。数据增强通过对原始数据进行一系列的变换和修改,生成新的训练样本,从而扩展训练集的规模和多样性。常用的数据增强的方法包括镜像翻转、旋转、平移、剪裁、颜色变换等,这些操作可以在不改变图像语义信息的情况下引入一些变化,使模型具有更好的鲁棒性。
数据增强使用的好处,降低数据采集和数据标记的成本,通过赋予模型更多的多样性和灵活性来改进模型的泛化,提高模型在预测中的准确性,因为它使用更多的数据来训练模型,减少数据的过拟合,通过增加少数类中的样本的来处理数据集中的不平衡。
epochs = 150
batch_size = 256
test_size = 0.1
optimizer = keras.optimizers.Adam(learning_rate=0.001)# 1.3 数据增强
X_train, X_val, y_train, y_val = train_test_split(features_train, labels, test_size=test_size, random_state=42)
train_datagen = ImageDataGenerator(rotation_range=18,width_shift_range=0.2,height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=False,
)train_datagen.fit(X_train)train_generator = train_datagen.flow(x=X_train, y=y_train,batch_size=batch_size)
这段代码使用ImageDataGenerator类来生成一个数据增强器,数据增强器被定义为train_datagen,其中使用了一些不同的参数来指定要应用的数据增强操作,如旋转、平移、缩放等。train_datagen.fit(X_train)训练train数据集,train_generator被定义为一个生成器,它可以按批次生成增强后的训练数据
4. 构建深度学习模型🤭
Keras是一个用Python编写的深度学习的API,运行在机器学习平台Tensorflow之上,它的目的是为了让开发者能够快速实验和享受愉快的开发体验。
4.1. 构建模型
首先导入Sequential类,用于创建一个顺序模型,即按照顺序添加各种层的神经网络的模型。
model.add(Conv2D(32,3,activation=‘relu’,padding=‘same’,input_shape=(28,28,1)))这句代码表示向模型添加一个二维卷积层,该层有32个卷积核,每个卷积核的大小是33,激活函数是“relu”,填充方式是same,即保持输入和输出的尺寸相同,输入的形状是(28,28,1),即2828的灰度图像。
下一句代码相似,只是不需要指定输入的形状,因为模型可以自动推断出来。
model.add(MaxPooling2D(2))这句代码是添加一个最大池化层,该层可以对输入的特征图进行下采样,减少参数的数量,提高模型的效率,参数“2”表示池化串口的大小是2*2。
接下来是做两次几乎重复操作,仅仅增加卷积核的数量,最后一次向模型中添加一个Flatten层,该层可以将多维的输入一维化,方便后面连接全连接层。
model.add(Dense(512,activation=‘relu’));model.add(Dropout(0.2));这两句代码向模型中添加一个全连接层,该层有512个神经元,激活函数是“relu”,然后向模型中添加一个Dropout层。
model.add(Dense(10,activation=‘softmax’))这句代码是向模型中添加一个全连接层,该层有10个神经元,激活函数是“softmax”,用于输出10个类别的概率分布。
最后打印出模型的结构和参数数量。
这就是基本的机器学习中的神经网络训练。训练完这段代码后,我迫切的像安装jupter notebook,因为pycharm每次训练实在太花时间了。
model = Sequential()model.add(Conv2D(32,3,activation='relu',padding='same',input_shape=(28,28,1)))
model.add(Conv2D(32,3, activation = 'relu', padding ='same'))
model.add(MaxPooling2D(2))model.add(Conv2D(64,3,activation='relu',padding='same'))
model.add(Conv2D(64,3, activation = 'relu', padding ='same'))
model.add(MaxPooling2D(2))model.add(Conv2D(128, 3, padding = 'same', activation = 'relu'))
model.add(Conv2D(128, 3, padding = 'same', activation = 'relu'))
model.add(Flatten())model.add(Dense(512,activation='relu'))
model.add(Dropout(0.2))model.add(Dense(10,activation='softmax'))model.summary()
打印模型的结构以及其各参数。
![](https://img-blog.csdnimg.cn/img_convert/694e00d85b1d2578134cc7eddf382722.jpeg)
4.2. 定义回调函数
这段代码定义了两个回调函数:
第一个是learning_rate_reduction,用于验证集准确率停止提高时,降低学习率。监控的指标是验证准确率,如果在2个周期内没有提高,就出发学习率衰减,学习率衰减的因子是0.5,学习率的最小值是0.00001,学习率不能低于这个值。
第二个是early_stoping,用于在验证集损失停止下降时,提前结束训练。监控的指标是验证集损失,如果在5个周期内没有下降,就提前结束训练,可以恢复最佳权重。
learning_rate_reduction = ReduceLROnPlateau(monitor = 'val_accuracy',patience=2,factor=0.5,min_lr = 0.00001,verbose = 1)early_stoping = EarlyStopping(monitor='val_loss',patience= 5,restore_best_weights=True,verbose=0)
4.3. 编译训练模型
这段代码首先是编译一个神经网络模型,选择“Adam”算法作为优化器,用于更新模型的权重;“分类交叉熵”作为损失函数,用于衡量模型的预测和真是标签之间的差异;评估指标选择“accuracy”准确率,用于评估模型的性能。
然后就开始训练一个神经网络模型,用训练数据和验证数据来更新模型的权重,以提高模型的性能。
使用上面定义的两个函数作为回调函数,每次训练的批量大小是10,即每次使用10个样本来更新权重,训练的总周期数是10,即重复训练30次。
model.compile(optimizer='adam',loss='categorical_crossentropy',metrics=['accuracy'])digit = model.fit(X_train,y_train,validation_data=(X_val,y_val),callbacks=[learning_rate_reduction,early_stoping],batch_size = 10,epochs = 30,verbose=1)
模型最多训练30次,但是我们设定了提前停止的函数,如果val_accuracy连续5次没有出现下降就停止,模型训练时的精确度为0.9995。
4.4. 评估训练和验证数据的准确性和损失😘
分别使用evaluate函数对训练数据和测试数据进行评估,以计算模型的性能指标。
loss,acc = model.evaluate(X_train,y_train,batch_size = 10, verbose = 0)print('The accuracy of the model for training data is:',acc*100)
print('The Loss of the model for training data is:',loss)# Evaluvate for validation generator
loss,acc = model.evaluate(X_val,y_val,batch_size = 10, verbose = 0)print('The accuracy of the model for validation data is:',acc*100)
print('The Loss of the model for validation data is:',loss)
使用evaluate函数进行模型评估,train数据集的精确度和损失度分别为99.833和0.00534;test数据集的精确度和损失度分别为99.1369和0.40。
4.5. 绘制损失和准确率曲线图🥰
将模型训练的历史记录转换为一个数据框,包括训练集和验证集的损失和准确率。
error = pd.DataFrame(digit.history)plt.figure(figsize=(18,5),dpi=200)
sns.set_style('darkgrid')plt.subplot(121)
plt.title('Cross Entropy Loss',fontsize=15)
plt.xlabel('Epochs',fontsize=12)
plt.ylabel('Loss',fontsize=12)
plt.plot(error['loss'])
plt.plot(error['val_loss'])plt.subplot(122)
plt.title('Classification Accuracy',fontsize=15)
plt.xlabel('Epochs',fontsize=12)
plt.ylabel('Accuracy',fontsize=12)
plt.plot(error['accuracy'])
plt.plot(error['val_accuracy'])plt.show()
左边的那幅图是交叉熵损失降低的图,右边是分类的精确度。前面的几轮训练,交叉熵损失飞快降低,分类精确度也对应增加,这是因为前几轮训练会非常规范的数据的精确度,随着训练的轮次增加,模型训练精度逐渐放缓。
5. 数据预测📊
(终于到预测啦🎉)
现在将预测和评估创建的测试数据集的标签。
batch_size表示每次预测的批量大小。
使用np.argmax函数,从result的每一行中找出最大数值的下标,返回一个一维数组。
result = model.predict(X_test, batch_size = 10,verbose = 0)y_pred = np.argmax(result, axis = 1)
5.1. 评估模型性能
评估测试数据的准确率,用百分比表示,然后打印测试数据的损失。
loss,acc = model.evaluate(X_test,y_test, batch_size = 10, verbose = 0)print('The accuracy of the model for testing data is:',acc*100)
print('The Loss of the model for testing data is:',loss)
测试集的精确度是98.869,损失率是0.044.
5.2. 创建测试数据的分类报告
print(classification_report(y_true, y_pred,target_names=labels))
0-9这十个数字预测数据的精度都达到了98%以上,训练结果很不错🎊
5.3. 创建测试数据的混淆矩阵
混淆矩阵是一种用于评估分类模型性能的表格,它显示了模型对每个类别的预测情况,以及预测正确和错误的数量。
使用了confusion_matrix函数,根据真实标签y_true和预测标签y_pred,计算混淆矩阵。
混淆矩阵绘制为热力图,使用seaborn库的heatmap完成。
confusion_mtx = confusion_matrix(y_true,y_pred)sns.set_style('ticks')
f,ax = plt.subplots(figsize = (20,8),dpi=200)
sns.heatmap(confusion_mtx, annot=True, linewidths=0.1, cmap = "gist_yarg_r", linecolor="black", fmt='.0f', ax=ax,cbar=False, xticklabels=labels, yticklabels=labels)plt.xlabel("Predicted Label",fontsize=10)
plt.ylabel("True Label",fontsize=10)
plt.title("Confusion Matrix",fontsize=13)plt.show()
5.4. 预测测试数据
test_data = reshape(test)# prediction
pred = model.predict(test_data, batch_size = 10,verbose = 0)prediction = np.argmax(pred, axis = 1)submission = pd.read_csv('D:\WorkSpace\Kaggle\DigitRecognizer\data\OutputData\sample_submission.csv')submission['Label'] = predictionsubmission.head()submission.to_csv(r'D:\WorkSpace\Kaggle\DigitRecognizer\data\OutputData\predict.csv,index=False)
打印一下需要提交数据的格式。
5.5. 提交数据
在kaggle上提交答案,看一下能对多少。
公榜上是329名,还是大佬的模型好啊。
这个题对于我来说很难,深度学习我也没有学习过,更多的是去参考了一些大佬的文章,跑通了baseline,也只是写文章记录下来。
前两天听讲座,认识一个kaggle大佬,参加kaggle竞赛,拿了很多奖金,今年秋招,拿了一个25k的offer,这是我努力的方向,学习目标,加油!🥰