Kaggle入门赛-Digit Recognizer(手写数字识别)


img
Kaggle入门赛-手写数字识别所使用的数据集MNIST是计算机视觉上的“Hello world”数据集。这个经典的手写图像数据集一直作为基准分类算法的基础。随着新的机器学习技术的出现,MNIST仍然是研究人员和学习者的可靠资源。

本文所介绍的算法通过使用一个多层感知机来完成手写数字的识别。本文通过数据预处理与可视化、模型搭建与训练、模型结果预测等方面来介绍如何从kaggle下载数据集,并通过处理后的数据集训练模型,再到最后如何将模型预测的结果提交到kaggle。

Digit Recognizer(手写数字识别)比赛网址:https://www.kaggle.com/competitions/digit-recognizer/

1、数据处理与可视化

通过kaggle官网下载手写数据识别的数据集,如下图所示,下载的数据集有三个文件分别为train.csv,test.csv,sample_submission.csv,其中train.csv为训练集包含标签和手写数字图像,test.csv为测试集只包含手写数字图像不包含标签,sample_submission.csv为提交示例。更多详细的内容可查看官方的数据集介绍。

img

首先导入所使用的相关包。

import pandas as pd
import numpy as np
import torch
import torch.utils.data as data
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torch import nn
%matplotlib inline

导入训练集,并输出训练集前五条数据,可以看出训练集的第一列为标签值,后面783列为28×28图像的像素值的展开后的格式,训练集共有42000条数据。

df = pd.read_csv('train.csv')
print(df.head())
print(df['label'].count())
   label  pixel0  pixel1  pixel2  pixel3  pixel4  pixel5  pixel6  pixel7  \
0      1       0       0       0       0       0       0       0       0   
1      0       0       0       0       0       0       0       0       0   
2      1       0       0       0       0       0       0       0       0   
3      4       0       0       0       0       0       0       0       0   
4      0       0       0       0       0       0       0       0       0   

   pixel8  ...  pixel774  pixel775  pixel776  pixel777  pixel778  pixel779  \
0       0  ...         0         0         0         0         0         0   
1       0  ...         0         0         0         0         0         0   
2       0  ...         0         0         0         0         0         0   
3       0  ...         0         0         0         0         0         0   
4       0  ...         0         0         0         0         0         0   

   pixel780  pixel781  pixel782  pixel783  
0         0         0         0         0  
1         0         0         0         0  
2         0         0         0         0  
3         0         0         0         0  
4         0         0         0         0  

[5 rows x 785 columns]
42000

然后对训练集进行处理,将标签与图像分开。然后将标签与图像的值转为pytorch中的tensor,数据类型为浮点型。由于是分类任务,因此将标签转为one-hot编码。

首先从数据中将标签取出,然后将数据类型转为tensor。

#从数据中将标签取出,然后将数据类型转为tensor
labels=torch.from_numpy(np.array(df['label']))
labels
tensor([1, 0, 1,  ..., 7, 6, 9])

然后将标签值转为one-hot编码。torch.sparse实现稀疏张量,然后通过eye函数生成一个10维的单位矩阵,最后通过index_select(input, dim, index)函数生成one-hot编码,该函数在dim维度上将input中索引为index的值保存下来,从而得到labels的one-hot编码。

#转为one-hot
#生成单位矩阵
ones = torch.sparse.torch.eye(10)
print(ones)
#根据指定索引和维度保留单位矩阵中的一条数据即为one-hot编码
label_one_hot=ones.index_select(0,labels)
print(label_one_hot[0],labels[0])
tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])
tensor([0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]) tensor(1)

将训练集中的图像部分转为tensor的浮点类型

df.pop('label')
#将训练集转为tensor
imgs=torch.from_numpy(np.array(df))
imgs = imgs.to(torch.float32)

定义构建数据集以及训练时所用的一些参数

#相关参数
#训练时的批量大小
train_batch_size=64
#训练轮次
num_epoches=20
#学习率
lr=0.01

通过DataLoader构造一个Pytorch数据迭代器,获得指定batch_size的数据迭代器,便于在训练时取出指定batch_size的训练数据。

#构造一个Pytorch数据迭代器
def load_array(data_arrays,batch_size,is_train=True):
    #加星号说明为元组
    #TensorDataset 可以用来对 tensor 进行打包
    dataset=data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset,batch_size,shuffle=is_train)
train_data=load_array((imgs,label_one_hot), batch_size=train_batch_size)

数据可视化,从生成的数据中随机抽取进行可视化并展示标签值。

#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
exaples=enumerate(train_data)
#next() 返回迭代器的下一个项目
batch_idx,(example_img,example_label) = next(exaples)
fig=plt.figure()
for i in range(6):
    plt.subplot(2,3,i+1)
    plt.tight_layout()
    plt.imshow(example_img[i].reshape(28,28),cmap='gray',interpolation='none')
    _,prd=example_label[i].max(0)
    plt.title('Ground Truth:{}'.format(prd))
    plt.xticks([])
    plt.yticks([])


png

2、模型搭建与训练

将数据处理完成之后,接下来就是定义模型,并使用处理好的数据对模型进行训练的阶段了,在此使用了一个多层感知机网络进行手写数字识别。

首先搭建用于模型训练的网络模型。在这里使用了一个三层的多层感知机,在每个线性层之后都使用了批量归一化以防止梯度消失的问题,激活函数选用了ReLU激活函数也更好的避免了梯度消失的问题。

class Net(nn.Module):
    def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
        super(Net,self).__init__()
        self.layer1=nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.BatchNorm1d(n_hidden_1))
        self.layer2=nn.Sequential(nn.Linear(n_hidden_1,n_hidden_2),nn.BatchNorm1d(n_hidden_2))
        self.layer3=nn.Sequential(nn.Linear(n_hidden_2,out_dim))
    def forward(self,x):
        x=F.relu(self.layer1(x))
        x=F.relu(self.layer2(x))
        x=self.layer3(x)
        #x=F.softmax(self.layer3(x))
        return x

下面就可以实例化我们定义的模型,手写数字图片共有784个像素值,因此输入为784维,两个隐藏层的大小分别问300和100,最后输出层维度为10将数字分为0-9十类。为加快训练速度并在有GPU的条件下,可将模型放到GPU上训练。

#检测是否有可用的设备
device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

#实例化网络
model=Net(784,300,100,10)
model.to(device)
Net(
  (layer1): Sequential(
    (0): Linear(in_features=784, out_features=300, bias=True)
    (1): BatchNorm1d(300, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (layer2): Sequential(
    (0): Linear(in_features=300, out_features=100, bias=True)
    (1): BatchNorm1d(100, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  )
  (layer3): Sequential(
    (0): Linear(in_features=100, out_features=10, bias=True)
  )
)

定义损失函是和优化器,损失函数采用交叉熵损失函数,优化器使用Adam,该优化算法可以动态改变学习率。

#损失函数和优化器
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=lr,betas=(0.9,0.99))

然后就可以训练训练我们的模型了

#训练网络
losses=[]
acces=[]

for epoch in range(num_epoches):
    train_loss=0
    train_acc=0
    model.train()
    for img,label in train_data:
        #将图片和标签放入GPU设备
        img=img.to(device)
        label=label.to(device)
        #计算结果
        out=model(img)
        #计算损失
        loss=criterion(out,label)
        #将梯度清零
        optimizer.zero_grad()
        #反向传播
        loss.backward()
        #更新网络参数
        optimizer.step()
        
        #记录训练损失
        train_loss+=loss.item()
        #记录训练准确度
        _,pred=out.max(1)
        l,pred1=label.max(1)
        num_correct=(pred==pred1).sum().item()
        acc=num_correct/img.shape[0]
        train_acc+=acc
    losses.append(train_loss/len(train_data))
    acces.append(train_acc/len(train_data))
    print('epoch:{},Train Loss:{:.4f},Train Acc:{:.4f}'.format(epoch,train_loss/len(train_data),train_acc/len(train_data)))
epoch:0,Train Loss:0.2226,Train Acc:0.9309
epoch:1,Train Loss:0.1126,Train Acc:0.9655
epoch:2,Train Loss:0.0823,Train Acc:0.9739
epoch:3,Train Loss:0.0623,Train Acc:0.9799
epoch:4,Train Loss:0.0526,Train Acc:0.9829
epoch:5,Train Loss:0.0450,Train Acc:0.9854
epoch:6,Train Loss:0.0378,Train Acc:0.9879
epoch:7,Train Loss:0.0351,Train Acc:0.9889
epoch:8,Train Loss:0.0313,Train Acc:0.9897
epoch:9,Train Loss:0.0254,Train Acc:0.9911
epoch:10,Train Loss:0.0259,Train Acc:0.9912
epoch:11,Train Loss:0.0255,Train Acc:0.9920
epoch:12,Train Loss:0.0242,Train Acc:0.9924
epoch:13,Train Loss:0.0217,Train Acc:0.9935
epoch:14,Train Loss:0.0207,Train Acc:0.9933
epoch:15,Train Loss:0.0218,Train Acc:0.9930
epoch:16,Train Loss:0.0197,Train Acc:0.9942
epoch:17,Train Loss:0.0192,Train Acc:0.9942
epoch:18,Train Loss:0.0180,Train Acc:0.9941
epoch:19,Train Loss:0.0171,Train Acc:0.9943

最终的准确率达到了99%多,看起来结果很不错,但这只是在训练集上的结果,在测试集上就不可能达到这个结果了。

将训练准确度和训练损失数值进行可视化,直观观察训练的结果。

plt.title('train loss and acc')
plt.plot(np.arange(len(losses)),losses)
plt.plot(np.arange(len(losses)), losses, color='red',label='Train Loss')
plt.plot(np.arange(len(losses)),acces, color='green', label='Train Acc')
plt.legend()
<matplotlib.legend.Legend at 0x2ded4f62fc8>


png

3、模型预测结果

模型训练完成之后,就可以使用训练集中的数据生成最终的结果,然后将结果上传到kaggle就可以看到我们的模型在测试集上到底可以得到多少分数了。

首先我们先处理一下测试集,将数据转为tensor的float32类型。

#读取测试集
df_test = pd.read_csv('test.csv')
test_imgs=torch.from_numpy(np.array(df_test))
test_imgs = test_imgs.to(torch.float32)
test_imgs
tensor([[0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        ...,
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.]])

将测试集放到GPU上,然后传入训练好的模型中得到预测的结果,并转为DataFrame类型便于生成上传到kaggle的结果集。

#test_img=torch.unsqueeze(test_imgs[0],0)
test_imgs=test_imgs.to(device)
_,pre=model(test_imgs).max(1)
res={}
pre = pre.cpu().numpy()
pre_size=pre.shape[0]
num = [i for i in range(1,pre_size+1)]
res_df=pd.DataFrame({
    'ImageId':num,
    'Label':pre
})

查看结果的前五行。

res_df.head()
ImageIdLabel
012
120
239
349
453

将结果输出到csv文件中。

res_df.to_csv('res.csv',index=False)

最后,将结果上传到kaggle中,就可以查看到模型在测试集上的准确率。

img

最终使用多层感知机可以达到0.97846的结果,后面会在使用卷积神经网络测试一下可以达到多少准确率,后面在更新。。。

4、代码整合

数据处理、模型搭建、模型训练部分,去掉了数据可视化和训练结果可视化部分。

import pandas as pd
import numpy as np
import torch
import torch.utils.data as data
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torch import nn
%matplotlib inline

#读取数据
df = pd.read_csv('train.csv')
#从数据中将标签取出,然后将数据类型转为tensor
labels=torch.from_numpy(np.array(df['label']))
#转为one-hot
ones = torch.sparse.torch.eye(10)
label_one_hot=ones.index_select(0,labels)
#将标签给从数据集中删除,得到训练集的输入数据
df.pop('label')
#将训练集转为tensor
imgs=torch.from_numpy(np.array(df))
imgs = imgs.to(torch.float32)

#相关参数
train_batch_size=64
num_epoches=20
lr=0.01

#构造一个Pytorch数据迭代器
def load_array(data_arrays,batch_size,is_train=True):
    #加星号说明为元组
    #TensorDataset 可以用来对 tensor 进行打包
    dataset=data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset,batch_size,shuffle=is_train)
train_data=load_array((imgs,label_one_hot), batch_size=train_batch_size)

#模型搭建
class Net(nn.Module):
    def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
        super(Net,self).__init__()
        self.layer1=nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.BatchNorm1d(n_hidden_1))
        self.layer2=nn.Sequential(nn.Linear(n_hidden_1,n_hidden_2),nn.BatchNorm1d(n_hidden_2))
        self.layer3=nn.Sequential(nn.Linear(n_hidden_2,out_dim))
    def forward(self,x):
        x=F.relu(self.layer1(x))
        x=F.relu(self.layer2(x))
        x=self.layer3(x)
        #x=F.softmax(self.layer3(x))
        return x
    
#检测是否有可用的设备
device=torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

#实例化网络
model=Net(784,300,100,10)
model.to(device)

#损失函数和优化器
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=lr,betas=(0.9,0.99))

#训练网络
losses=[]
acces=[]

for epoch in range(num_epoches):
    train_loss=0
    train_acc=0
    model.train()
    #动态修改学习率
   # if epoch%5==0:
    #    optimizer.param_groups[0]['lr']*=0.1
    for img,label in train_data:
        img=img.to(device)
        label=label.to(device)
        
        out=model(img)
        loss=criterion(out,label)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        train_loss+=loss.item()
        
        _,pred=out.max(1)
        l,pred1=label.max(1)
        num_correct=(pred==pred1).sum().item()
        acc=num_correct/img.shape[0]
        train_acc+=acc
    losses.append(train_loss/len(train_data))
    acces.append(train_acc/len(train_data))
    print('epoch:{},Train Loss:{:.4f},Train Acc:{:.4f}'.format(epoch,train_loss/len(train_data),train_acc/len(train_data)))
epoch:0,Train Loss:0.2245,Train Acc:0.9318
epoch:1,Train Loss:0.1169,Train Acc:0.9640
epoch:2,Train Loss:0.0832,Train Acc:0.9728
epoch:3,Train Loss:0.0623,Train Acc:0.9805
epoch:4,Train Loss:0.0499,Train Acc:0.9840
epoch:5,Train Loss:0.0438,Train Acc:0.9859
epoch:6,Train Loss:0.0395,Train Acc:0.9873
epoch:7,Train Loss:0.0314,Train Acc:0.9895
epoch:8,Train Loss:0.0307,Train Acc:0.9899
epoch:9,Train Loss:0.0274,Train Acc:0.9910
epoch:10,Train Loss:0.0259,Train Acc:0.9915
epoch:11,Train Loss:0.0237,Train Acc:0.9919
epoch:12,Train Loss:0.0243,Train Acc:0.9922
epoch:13,Train Loss:0.0226,Train Acc:0.9927
epoch:14,Train Loss:0.0199,Train Acc:0.9937
epoch:15,Train Loss:0.0192,Train Acc:0.9937
epoch:16,Train Loss:0.0194,Train Acc:0.9934
epoch:17,Train Loss:0.0177,Train Acc:0.9944
epoch:18,Train Loss:0.0179,Train Acc:0.9947
epoch:19,Train Loss:0.0177,Train Acc:0.9944

结果预测以及导出为csv文件部分

#读取测试集
df_test = pd.read_csv('test.csv')
test_imgs=torch.from_numpy(np.array(df_test))
test_imgs = test_imgs.to(torch.float32)

#测试集预测部分
#传入GPU
test_imgs=test_imgs.to(device)
#计算结果
_,pre=model(test_imgs).max(1)
#将结果转为提交kaggle的格式
res={}
pre = pre.cpu().numpy()
pre_size=pre.shape[0]
num = [i for i in range(1,pre_size+1)]
res_df=pd.DataFrame({
    'ImageId':num,
    'Label':pre
})

#d导出为CSV文件
res_df.to_csv('res.csv',index=False)
  • 10
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是使用Python和Keras库来解决Kaggle Digit Recognizer的代码示例: 首先,导入必要的库: ```python import pandas as pd import numpy as np from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D from keras.optimizers import RMSprop from keras.preprocessing.image import ImageDataGenerator from sklearn.model_selection import train_test_split ``` 然后,读取和处理训练数据和测试数据: ```python train_data = pd.read_csv('train.csv') test_data = pd.read_csv('test.csv') # 将数据分成输入和输出 X_train = train_data.drop(['label'], axis=1) y_train = train_data['label'] # 将输入数据重塑为28x28像素 X_train = X_train.values.reshape(-1, 28, 28, 1) test_data = test_data.values.reshape(-1, 28, 28, 1) # 将像素值转换为浮点数并归一化 X_train = X_train.astype('float32') / 255 test_data = test_data.astype('float32') / 255 # 将输出数据转换为独热编码 y_train = pd.get_dummies(y_train).values ``` 接着,将数据分成训练集和验证集,设置数据增强器并构建卷积神经网络模型: ```python # 将数据分成训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.1) # 设置数据增强器 datagen = ImageDataGenerator( rotation_range=10, zoom_range = 0.1, width_shift_range=0.1, height_shift_range=0.1) # 构建卷积神经网络模型 model = Sequential() model.add(Conv2D(filters=32, kernel_size=(5,5), padding='Same', activation='relu', input_shape=(28,28,1))) model.add(Conv2D(filters=32, kernel_size=(5,5), padding='Same', activation='relu')) model.add(MaxPool2D(pool_size=(2,2))) model.add(Dropout(0.25)) model.add(Conv2D(filters=64, kernel_size=(3,3), padding='Same', activation='relu')) model.add(Conv2D(filters=64, kernel_size=(3,3), padding='Same', activation='relu')) model.add(MaxPool2D(pool_size=(2,2), strides=(2,2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(256, activation="relu")) model.add(Dropout(0.5)) model.add(Dense(10, activation="softmax")) # 定义优化器和损失函数 optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0) model.compile(optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"]) ``` 最后,使用训练集和验证集来训练和评估模型,并对测试数据进行预测: ```python # 训练模型 history = model.fit_generator(datagen.flow(X_train, y_train, batch_size=64), epochs=30, validation_data=(X_val, y_val), verbose=2) # 在验证集上评估模型 score = model.evaluate(X_val, y_val, verbose=0) print("Validation loss:", score[0]) print("Validation accuracy:", score[1]) # 对测试数据进行预测 predictions = model.predict(test_data) ``` 这就是一个简单的使用卷积神经网络和数据增强器来解决Kaggle Digit Recognizer的代码示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值