Cai Caixue Paddle Parte 4: Mejora de las redes neuronales convolucionales para construir el reconocimiento de números escritos a mano

Prefacio:

        En "Cai Cai Xue Paddle Part 3", usamos la red neuronal convolucional para construir el reconocimiento de dígitos escritos a mano, pero desafortunadamente, su tasa de precisión es asombrosa. Pensé que era un diamante, pero no pudo hacer el trabajo sucio. ¿Cuál es la razón?

        Diferentes tareas de aprendizaje profundo requieren sus propias funciones de pérdida apropiadas. El reconocimiento de dígitos escritos a mano es una tarea de clasificación, y el uso del error cuadrático medio como función de pérdida de la tarea de clasificación carece de lógica y efecto.

        En esta publicación de blog, modificamos la función de calcular la pérdida, desde el error cuadrático medio (usado a menudo en el problema de regresión F.square_error_cost()) hasta el error de entropía cruzada (usado a menudo en el problema de clasificación de F.cross_entropy)

1. Carga de datos

1. Cree un nuevo archivo: CNNCrossEntropy.py

La diferencia entre la carga de datos y la anterior radica en el tipo de etiqueta, la anterior es un tipo de datos de coma flotante, y esta se cambia a entero:

etiqueta = np.reshape(etiquetas[i], [1]).astype('int64')
import paddle
from paddle.nn import Conv2D, MaxPool2D, Linear
import paddle.nn.functional as F
import gzip
import json
import random
import numpy as np

# 定义数据集读取器
def load_data(mode='train'):

    # 加载数据
    datafile = './../work/mnist.json.gz'
    print('loading mnist dataset from {} ......'.format(datafile))
    data = json.load(gzip.open(datafile))
    print('mnist dataset load done')

    # 读取到的数据区分训练集,验证集,测试集
    train_set, val_set, eval_set = data

    # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
    IMG_ROWS = 28
    IMG_COLS = 28

    if mode == 'train':
        # 获得训练数据集
        imgs, labels = train_set[0], train_set[1]
    elif mode == 'valid':
        # 获得验证数据集
        imgs, labels = val_set[0], val_set[1]
    elif mode == 'eval':
        # 获得测试数据集
        imgs, labels = eval_set[0], eval_set[1]
    else:
        raise Exception("mode can only be one of ['train', 'valid', 'eval']")

    #校验数据
    imgs_length = len(imgs)
    assert len(imgs) == len(labels), \
        "length of train_imgs({}) should be the same as train_labels({})".format(
            len(imgs), len(labels))

    # 定义数据集每个数据的序号, 根据序号读取数据
    index_list = list(range(imgs_length))
    # 读入数据时用到的batchsize
    BATCHSIZE = 100

    # 定义数据生成器
    def data_generator():
        if mode == 'train':
            random.shuffle(index_list)
        imgs_list = []
        labels_list = []
        for i in index_list:
            img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
            label = np.reshape(labels[i], [1]).astype('int64')
            imgs_list.append(img)
            labels_list.append(label)
            if len(imgs_list) == BATCHSIZE:
                yield np.array(imgs_list), np.array(labels_list)
                imgs_list = []
                labels_list = []

        # 如果剩余数据的数目小于BATCHSIZE,
        # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
        if len(imgs_list) > 0:
            yield np.array(imgs_list), np.array(labels_list)

    return data_generator

2. Diseño del modelo

1. Continúe agregando el código del modelo en el archivo: CNNCrossEntropy.py:

class MNIST(paddle.nn.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        #二维卷积层
        self.conv1 = Conv2D(in_channels=1,out_channels=20,kernel_size=5,stride=1,padding=2)
        #最大池化层
        self.max_pool1 = MaxPool2D(kernel_size=2,stride=2)
        #二维卷积层
        self.conv2 = Conv2D(in_channels=20, out_channels=20,kernel_size=5,stride=1,padding=2)
        #最大池化层
        self.max_pool2 = MaxPool2D(kernel_size=2,stride=2)
        #全连接层
        self.fc = Linear(in_features=980,out_features=10)

    def forward(self, input):
        x = self.conv1(input)
        x = F.relu(x)
        x = self.max_pool1(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = self.max_pool2(x)
        x = paddle.reshape(x, [x.shape[0], 980])
        x = self.fc(x)
        return x

2. Dado que es una clasificación, la salida de la capa completamente conectada ya no es un valor específico, sino la probabilidad de 10 valores. Cuando tomemos el valor más adelante, buscaremos el que tenga la mayor probabilidad correspondiente a estos diez valores Qué es, y luego tome el número correspondiente a la posición más grande.

3. Formación modelo

1. Cree un nuevo archivo CNNTrainWithCrossEntropy.py

from CNNCrossEntropy import load_data,MNIST
import paddle
import paddle.nn.functional as F

def train(model):
    model.train()
    #调用加载数据的函数,获得MNIST训练数据集
    train_loader = load_data('train')
    # 使用SGD优化器,学习率设置为0.01
    opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
    # 训练10轮
    EPOCH_NUM = 10
    loss_list = []
    for epoch_id in range(EPOCH_NUM):
        for batch_id, data in enumerate(train_loader()):
            #准备数据
            images, labels = data
            images = paddle.to_tensor(images)
            labels = paddle.to_tensor(labels)
            #前向计算的过程
            predicts = model(images)
            #计算损失,取一个批次样本损失的平均值
            loss = F.cross_entropy(predicts, labels)
            avg_loss = paddle.mean(loss)
            #每训练200批次的数据,打印下当前Loss的情况
            if batch_id % 200 == 0:
                loss = avg_loss.numpy()[0]
                loss_list.append(loss)
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, loss))
            #后向传播,更新参数的过程
            avg_loss.backward()
            # 最小化loss,更新参数
            opt.step()
            # 清除梯度
            opt.clear_grad()
    #保存模型参数
    paddle.save(model.state_dict(), 'mnist.crossentropy.pdparams')
    return loss_list

model = MNIST()
train(model)

2. Aquí, modifique la función de pérdida para cruzar la entropía

pérdida = F.cross_entropy(predice, etiquetas)

4. Validación del modelo

1. Cree un nuevo archivo: CNNCrossEntropyEval.py

import paddle
from CNNCrossEntropy import MNIST, load_data
import numpy as np

model = MNIST()
params_file_path = 'mnist.crossentropy.pdparams'
# 加载模型参数
param_dict = paddle.load(params_file_path)
model.load_dict(param_dict)
# 定义预测过程
model.eval()

# 加载测试集
test_loader = load_data('eval')
success = 0
error = 0
for batch_id, data in enumerate(test_loader()):
    images, labels = data
    images = paddle.to_tensor(images)
    results = model(images)
    results = results.numpy().astype('int32')
    labels = labels.astype('int32')
    for i in range(0,100):
        label = labels[i][0]
        lab = results[i]
        result = np.argsort(lab)[-1]
        if (label == result) :
            success = success + 1
        else:
            error = error + 1
#  预测输出取整,即为预测的数字,打印结果
print("本次预测的正确的数量是{}, 错误的数量是{}".format(success, error))

2. Resultado de la predicción:

cargando el conjunto de datos mnist desde ./../work/mnist.json.gz ......
carga del conjunto de datos mnist hecho
El número correcto de esta predicción es 9831, y el número incorrecto es 169

Resumir:

1. Cambie un poco la función de pérdida, de modo que nuestra tasa de precisión de predicción haya aumentado en varios órdenes de magnitud, y la tasa de error se controle por debajo del 2%. Esta tasa de error no representa absolutamente ningún problema cuando se usa en la industria.

2. np.argsort(a): organiza los elementos en a de menor a mayor y devuelve la salida del índice correspondiente (índice) antes de la disposición. Usamos el modelo para predecir la probabilidad de que una imagen sea cada número del 0 al 9, luego clasificamos la probabilidad y finalmente sacamos las coordenadas correspondientes al valor con la probabilidad más alta, y las coordenadas son los números correspondientes.

3. En el cálculo directo, aclaramos que la función de activación de la capa convolucional usa Relu, entonces, ¿cuál es la función de activación de la capa completamente conectada?

Supongo que te gusta

Origin blog.csdn.net/duzm200542901104/article/details/127869886
Recomendado
Clasificación