CNN实战-手写数字识别

  • 数据集下载地址:https://www.kaggle.com/c/digit-recognizer/data
  • 本项目以CNN实战流程讲解为主,非准确率效果提升技巧项目.
  • 本项目流程规范为作者个人理解,不做指导性建议,读者可根据个人理解梳理.

Step1-数据处理

  • 通常是项目中较为麻烦的代码,我们需要把原始数据转变为模型可以处理的格式
def load_data():
    """
    加载数据
    """
    train = pd.read_csv('./data/train.csv')
    data = train.drop('label', axis=1)
    test = pd.read_csv('./data/test.csv')
    # 测试集 转为 tensor
    test_data = t.from_numpy(test.values).float()
    data = data.values
    y = train['label'].values
    y = t.from_numpy(y).long()
    data = t.from_numpy(data).float()
    # 最后进行误差反向传递的时候, 一次性将所有 variable 里面的修改幅度 (梯度) 都计算出来, tensor 没有这个功能
    data, y = Variable(data), Variable(y)
    return data, y, test_data

def to_image(data):
    """
    将数据转为二维, 我们的图片是 28 * 28 的。
    :param data:
    :return:
    """
    data = data.view(-1, 1, 28, 28)
    return data

Setp2-定义网络

class conv_net(t.nn.Module):
    """
    卷积神经网络, 上篇文章中已经对网络结构做过介绍,直接使用
    """

    def __init__(self):
        super(conv_net, self).__init__()
        self.conv1 = t.nn.Sequential(
            t.nn.Conv2d(1, 10, 5),
            t.nn.MaxPool2d(2),
            t.nn.ReLU(),
            t.nn.BatchNorm2d(10)
        )
        self.conv2 = t.nn.Sequential(
            t.nn.Conv2d(10, 20, 5),
            t.nn.MaxPool2d(2),
            t.nn.ReLU(),
            t.nn.BatchNorm2d(20)
        )
        self.fc1 = t.nn.Sequential(
            t.nn.Linear(320, 60),
            t.nn.Dropout(0.5),
            t.nn.ReLU()
        )
        self.fc2 = t.nn.Sequential(
            t.nn.Linear(60, 20),
            t.nn.Dropout(0.5),
            t.nn.ReLU()
        )
        self.fc3 = t.nn.Linear(20, 10)
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(-1, 320)
        x = self.fc1(x)
        x = self.fc2(x)
        x = self.fc3(x)
        return x
# 定义网络结构
net = conv_net()

Step3 - 定义损失函数

# 定义模型损失函数
criterion = t.nn.CrossEntropyLoss()

Step4 - 定义优化器

# 定义优化器
optim = t.optim.Adam(net.parameters(), lr=0.001, weight_decay=0.0)

Step5 - 模型训练

因为这里 test_data 没有 label,验证模型效果需要提交到 kaggle 验证,作者这里从 data 中拆分出 10% 当作验证集。想尝试的,也可以直接使用 test_data,提交到 kaggle 验证模型效果。

# 加载数据,处理数据
data, y, test_data = load_data()
data = to_image(data)
# test_data = to_image(test_data)
test_data = data[37800:]
test_y = y[37800:]
data = data[0:37800]
y = y[0:37800]
# 模型训练
EPOCHS = 100
BATCH_SIZE = 500
LR = 0.001
for epoch in tqdm(range(EPOCHS)):

    # 动态调整学习率
    if epoch % 100 == 0:
        for param_group in optim.param_groups:
            LR = LR * 0.9
            param_group['lr'] = LR

    index = 0
    # 分批次训练
    for i in tqdm(range(int(len(data) / BATCH_SIZE)), total=int(len(data) / BATCH_SIZE)):
        batch_x = data[index:index + BATCH_SIZE]
        batch_y = y[index:index + BATCH_SIZE]
        # 模型预测
        prediction = net.forward(batch_x)
        # 计算 loss
        loss = criterion(prediction, batch_y)
        # 我们需要在开始进行反向传播之前将梯度设置为零,因为PyTorch 会在随后的向后传递中累积梯度。
        # 当网络参量进行反馈时,梯度是被积累的而不是被替换掉;但是在每一个batch时毫无疑问并不需要将两个batch的梯度混合起来累积,因此这里就需要每个batch设置一遍zero_grad
        optim.zero_grad()
        # 方向传播
        loss.backward()
        # 参数更新
        optim.step()

        index = index + BATCH_SIZE
        print(loss.item())
# 保存模型
t.save(net.state_dict(), 'conv.pth')

Step6 - 验证模型效果

# 验证模型效果
test_data = Variable(test_data)
result = t.Tensor()
#  分段进行预测,节省内存
index = 0
for i in tqdm(range(int(test_data.shape[0] / BATCH_SIZE)), total=int(test_data.shape[0] / BATCH_SIZE)):
    label_prediction = net(test_data[index:index + BATCH_SIZE])
    index += BATCH_SIZE
    result = t.cat((result, label_prediction), 0)
#  t.max返回一个元祖,第一个元素是最大元素值,第二个元素是最大元素位置
_,pred = t.max(result.data, 1)
train_correct = (pred == test_y).sum().item()
accuracy = train_correct/len(pred)
print(accuracy)

一个完整的深度学习项目大致流程如此,熟悉基本套路之后我们才能在此基础之上不断改进。

  • 文中具体代码细节,后续不定时补充

你可能感兴趣的:(CNN实战-手写数字识别)