Цифровое распознавание с помощью летающих лопастей

глубокое обучение

Автор: Ян Цин

Распознавание цифр — классический случай машинного обучения и глубокого обучения. По этой причине этот случай также реализуется с использованием платформы глубокого обучения летающего весла. Набор данных, используемый в этом примере, взят из MNIST.


Рисунок 1. Пример изображения MNIST

В этой статье мы начнем с простой регрессионной модели Softmax, познакомим вас с распознаванием рукописных символов и покажем, как улучшить модель с помощью многослойного персептрона (MLP) и сверточной нейронной сети (CNN) для оптимизации эффекта распознавания.

намекать:Код этой статьи был опубликован на платформе онлайн-экспериментов. Пожалуйста, обратите внимание на общедоступную учетную запись WeChat этой статьи (отсканируйте QR-код в конце статьи) и ответьте: имя + номер мобильного телефона + «дело». чтобы получить это.

Обзор

Во-первых, поймите следующие определения, используемые в этой статье:

  • X, представляющий вход: изображение MNIST представляет собой двумерное изображение 28 × 28. Для расчета мы преобразуем его в 784-мерный вектор, то есть X=(x_0,x_1,…,x_{783}).
  • Y, представляющий вывод, предсказанный классификатором: вывод классификатора представляет собой число 10-го класса (0-9), то есть Y=(y_0,y_1,…,y_9), каждое измерениеy_iПредставляет вероятность того, что изображение классифицируется как цифра класса i.
  • Label, реальная метка, представляющая цифровое изображение: Label=(l_0,l_1,…,l_9), который тоже 10-мерный, но только один признак равен 1, а остальные 0, то есть для представления чисел используется разреженная матрица. Например, число на картинке равно 2, тогда соответствующий образец равен (0,0,1,0,…,0)

Регрессия Softmax

Самая простая регрессионная модель Softmax состоит в том, чтобы сначала передать входной слой через полносвязный слой для получения функций, а затем напрямую передатьsoftmaxФункция вычисляет вероятности нескольких классов и выводит их.

X передается выходному слою и перед операцией активации умножается на соответствующий вес W и добавляется переменная смещения b, как показано в следующей формуле:

y_i = softmax(\sum_jW_{i,j}x_j + b_i)

в

softmax(x_i) = \frac{e^{x_i}}{\sum_je^{x_j}}

Для задачи мультиклассификации с N категориями задаются N выходных узлов, а N-мерный вектор преобразуется в N вещественных значений в диапазоне [0, 1] через softmax, соответственно представляющих вероятность того, что выборка принадлежит к этим N категориям. здесьy_iТо есть прогнозируемая вероятность того, что на картинке номер i.

В задачах классификации мы обычно используем функцию кросс-энтропийных потерь (кросс-энтропийных потерь), формула выглядит следующим образом:

L_{cross-entropy}(label, y) = -\sum_i label_i log(y_i)

Многослойный персептрон (MLP)

Модель регрессии Softmax использует простейшую двухслойную нейронную сеть, то есть только входной слой и выходной слой, поэтому ее подгоночные возможности ограничены. Чтобы добиться лучшего эффекта распознавания, мы рассматриваем возможность добавления нескольких скрытых слоев между входным и выходным слоями.

  1. После первого скрытого слоя мы можем получитьH_1=ϕ(W_1X+b_1), где ϕ представляет собой функцию активации, а распространенными функциями являются сигмовидная, тангенциальная или ReLU.
  2. После второго скрытого слоя мы можем получитьH_2=ϕ(W_2H_1+b_2).
  3. Наконец, пройдя выходной слой, мы получаемY=softmax(W_3H_2+b_3), что является окончательным результатом классификации.

Сверточная нейронная сеть (CNN)

В многослойной модели персептрона изображение расширяется до одномерного вектора и вводится в сеть, игнорируя положение и структурную информацию изображения, в то время как сверточная нейронная сеть может лучше использовать структурную информацию изображения. LeNet-5 — относительно простая сверточная нейронная сеть.На рисунке 2 показана ее структура: входное двумерное изображение проходит через два сверточных слоя в слой пула, затем в полносвязный слой и, наконец, использует классификацию softmax в качестве выходного этажа.


Рис. 2. Структура сверточной нейронной сети LeNet-5

Процесс работы

PaddlePaddle предоставляет модуль для автоматической загрузки данных MNIST в API.paddle.dataset.mnist. Загруженные данные находятся в/home/username/.cache/paddle/dataset/mnistВниз:

имя файла инструкция
train-images-idx3-ubyte Изображение обучающих данных, 60 000 единиц данных
train-labels-idx1-ubyte Метки обучающих данных, 60 000 единиц данных
t10k-images-idx3-ubyte Изображение тестовых данных, 10 000 единиц данных
t10k-labels-idx1-ubyte Метки тестовых данных, 10 000 единиц данных

Обзор жидкостного API

Fluid API — это новейший API PaddlePaddle, который упрощает настройку модели без ущерба для производительности и рекомендуется к использованию.

Ниже представлен обзор нескольких важных концепций Fluid API:

  1. inference_program: функция, которая указывает, как получать прогнозы из входных данных, здесь указывается сетевой поток.
  2. train_program: указывает, какinference_programи получить значение тегаlossФункция от , здесь указывается расчет потерь.
  3. optimizer_func: функция, определяющая конфигурацию оптимизатора. Оптимизатор отвечает за снижение потерь и управление обучением. Paddle поддерживает множество различных оптимизаторов.

Загрузите пакет Fluid API PaddlePaddle.

import os
from PIL import Image # 导入图像处理模块
import matplotlib.pyplot as plt
import numpy
import paddle # 导入paddle模块
import paddle.fluid as fluid

Конфигурация программных функций

нам нужно установитьinference_programфункция. Ниже показаны три разных классификатора, каждый из которых определен как функция Python. Нам нужно передать данные изображения в классификатор. Paddle предоставляет специальный слой для чтения данныхfluid.dataЭтаж. Давайте создадим слой данных для чтения изображения и подключим его к сети классификации.

  • Регрессия Softmax: результат классификации можно получить только через простой полносвязный слой с softmax в качестве функции активации.
def softmax_regression():
    """
    定义softmax分类器:
        一个以softmax为激活函数的全连接层
    Return:
        predict_image -- 分类的结果
    """
    # 输入的原始图像数据,大小为28*28*1
    img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
    # 以softmax为激活函数的全连接层,输出层的大小必须为数字的个数10
    predict = fluid.layers.fc(
        input=img, size=10, act='softmax')
    return predict
  • Многослойный персептрон: следующий код реализует многослойный персептрон с двумя скрытыми слоями (т. е. полностью связанными слоями). Функция активации двух скрытых слоев — ReLU, а функция активации выходного слоя — Softmax.
def multilayer_perceptron():
    """
    定义多层感知机分类器:
        含有两个隐藏层(全连接层)的多层感知器
        其中前两个隐藏层的激活函数采用 ReLU,输出层的激活函数用 Softmax

    Return:
        predict_image -- 分类的结果
    """
    # 输入的原始图像数据,大小为28*28*1
    img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
    # 第一个全连接层,激活函数为ReLU
    hidden = fluid.layers.fc(input=img, size=200, act='relu')
    # 第二个全连接层,激活函数为ReLU
    hidden = fluid.layers.fc(input=hidden, size=200, act='relu')
    # 以softmax为激活函数的全连接输出层,输出层的大小必须为数字的个数10
    prediction = fluid.layers.fc(input=hidden, size=10, act='softmax')
    return prediction
  • Сверточная нейронная сеть LeNet-5: входное двумерное изображение сначала проходит через два сверточных слоя в объединяющий слой, затем проходит через полносвязный слой и, наконец, использует полносвязный слой с softmax в качестве функции активации в качестве выходного слоя.
def convolutional_neural_network():
    """
    定义卷积神经网络分类器:
        输入的二维图像,经过两个卷积-池化层,使用以softmax为激活函数的全连接层作为输出层

    Return:
        predict -- 分类的结果
    """
    # 输入的原始图像数据,大小为28*28*1
    img = fluid.data(name='img', shape=[None, 1, 28, 28], dtype='float32')
    # 第一个卷积-池化层
    # 使用20个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
    conv_pool_1 = fluid.nets.simple_img_conv_pool(
        input=img,
        filter_size=5,
        num_filters=20,
        pool_size=2,
        pool_stride=2,
        act="relu")
    conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)
    # 第二个卷积-池化层
    # 使用50个5*5的滤波器,池化大小为2,池化步长为2,激活函数为Relu
    conv_pool_2 = fluid.nets.simple_img_conv_pool(
        input=conv_pool_1,
        filter_size=5,
        num_filters=50,
        pool_size=2,
        pool_stride=2,
        act="relu")
    # 以softmax为激活函数的全连接输出层,输出层的大小必须为数字的个数10
    prediction = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax')
    return prediction

Конфигурация программы обучения

Затем нам нужно настроить программу обученияtrain_program. Он начинается с предсказания классификатора. Во время обучения он будет рассчитываться из прогнозовavg_cost.

Примечание. Тренер должен возвращать массив, первый возвращаемый параметр должен бытьavg_cost. Тренер использует его для вычисления градиентов.

Не стесняйтесь изменять код, чтобы протестировать регрессию Softmax.softmax_regression, MLPи сверточные нейронные сетиconvolutional neural networkРазличные результаты между классификаторами.

def train_program():
    """
    配置train_program

    Return:
        predict -- 分类的结果
        avg_cost -- 平均损失
        acc -- 分类的准确率

    """
    # 标签层,名称为label,对应输入图片的类别标签
    label = fluid.data(name='label', shape=[None, 1], dtype='int64')

    # predict = softmax_regression() # 取消注释将使用 Softmax回归
    # predict = multilayer_perceptron() # 取消注释将使用 多层感知器
    predict = convolutional_neural_network() # 取消注释将使用 LeNet5卷积神经网络

    # 使用类交叉熵函数计算predict和label之间的损失函数
    cost = fluid.layers.cross_entropy(input=predict, label=label)
    # 计算平均损失
    avg_cost = fluid.layers.mean(cost)
    # 计算分类准确率
    acc = fluid.layers.accuracy(input=predict, label=label)
    return predict, [avg_cost, acc]

Конфигурация функции оптимизатора

В следующихAdam optimizer,learning_rate— скорость обучения, а ее размер связан со скоростью сходимости обучения сети.

def optimizer_program():
    return fluid.optimizer.Adam(learning_rate=0.001)

Конфигурация фидов набора данных

Далее приступаем к тренировочному процессу.paddle.dataset.mnist.train()иpaddle.dataset.mnist.test()Отдельные наборы данных для обучения и тестирования. Каждая из этих двух функций возвращает считыватель — считыватель в PaddlePaddle — это функция Python, которая возвращает генератор доходности Python при каждом вызове.

Следующая перетасовка представляет собой декоратор считывателя, который принимает считыватель A и возвращает другой считыватель B. читатель B читает каждый разbuffer_sizeПоместите обучающие данные в буфер, затем случайным образом перетасуйте их порядок и выведите их один за другим.

Пакет — это специальный декоратор, входом которого является считыватель, а выходом — пакетный считыватель. В PaddlePaddle считыватель выдает одну часть обучающих данных за раз, а пакетный считыватель выдает за раз мини-пакет.

# 一个minibatch中有64个数据
BATCH_SIZE = 64

# 每次读取训练集中的500个数据并随机打乱,传入batched reader中,batched reader 每次 yield 64个数据
train_reader = paddle.batch(
        paddle.reader.shuffle(
            paddle.dataset.mnist.train(), buf_size=500),
        batch_size=BATCH_SIZE)
# 读取测试集的数据,每次 yield 64个数据
test_reader = paddle.batch(
            paddle.dataset.mnist.test(), batch_size=BATCH_SIZE)

Выстроить тренировочный процесс

Теперь нам нужно построить тренировочный процесс. Программа обучения train_program, место и оптимизатор, определенные ранее, будут использоваться и будут включать в себя итерации обучения, проверку ошибки теста во время обучения и сохранение параметров модели, необходимых для прогнозирования.

Конфигурация обработчика событий

Мы можем отслеживать ход обучения во время обучения, вызывая функцию-обработчик. Здесь мы продемонстрируем две программы event_handler. Не стесняйтесь модифицировать Jupyter Notebook, чтобы увидеть разницу.

event_handler используется для вывода результатов обучения во время обучения

def event_handler(pass_id, batch_id, cost):
    # 打印训练的中间结果,训练轮次,batch数,损失函数
    print("Pass %d, Batch %d, Cost %f" % (pass_id,batch_id, cost))
from paddle.utils.plot import Ploter

train_prompt = "Train cost"
test_prompt = "Test cost"
cost_ploter = Ploter(train_prompt, test_prompt)

# 将训练过程绘图表示
def event_handler_plot(ploter_title, step, cost):
    cost_ploter.append(ploter_title, step, cost)
    cost_ploter.plot()

event_handler_plotМожет использоваться для рисования графиков во время обучения следующим образом:

начать обучение

может присоединиться к нашемуevent_handlerиdata reader, и вы можете приступить к обучению модели. Установите некоторые параметры, необходимые для работы, настройте описание данныхfeed_orderИспользуется для сопоставления каталога данных сtrain_programСоздать отзыв об ошибке во время обученияtrain_test

Определите структуру сети:

# 该模型运行在单个CPU上
use_cuda = True # 如想使用GPU,请设置为 True
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()

# 调用train_program 获取预测值,损失值,
prediction, [avg_loss, acc] = train_program()

# 输入的原始图像数据,名称为img,大小为28*28*1
# 标签层,名称为label,对应输入图片的类别标签
# 告知网络传入的数据分为两部分,第一部分是img值,第二部分是label值
feeder = fluid.DataFeeder(feed_list=['img', 'label'], place=place)

# 选择Adam优化器
optimizer = optimizer_program()
optimizer.minimize(avg_loss)

Задайте гиперпараметры для тренировочного процесса:

PASS_NUM = 5 #训练5轮
epochs = [epoch_id for epoch_id in range(PASS_NUM)]

# 将模型参数存储在名为 save_dirname 的文件中
save_dirname = "recognize_digits.inference.model"
def train_test(train_test_program,
                   train_test_feed, train_test_reader):

    # 将分类准确率存储在acc_set中
    acc_set = []
    # 将平均损失存储在avg_loss_set中
    avg_loss_set = []
    # 将测试 reader yield 出的每一个数据传入网络中进行训练
    for test_data in train_test_reader():
        acc_np, avg_loss_np = exe.run(
            program=train_test_program,
            feed=train_test_feed.feed(test_data),
            fetch_list=[acc, avg_loss])
        acc_set.append(float(acc_np))
        avg_loss_set.append(float(avg_loss_np))
    # 获得测试数据上的准确率和损失值
    acc_val_mean = numpy.array(acc_set).mean()
    avg_loss_val_mean = numpy.array(avg_loss_set).mean()
    # 返回平均损失值,平均准确率
    return avg_loss_val_mean, acc_val_mean

Создайте исполнителя:

exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())

Настройте main_program и test_program :

main_program = fluid.default_main_program()
test_program = fluid.default_main_program().clone(for_test=True)

Начать обучение:

lists = []
step = 0

for epoch_id in epochs:
    for step_id, data in enumerate(train_reader()):
        metrics = exe.run(main_program,
                          feed=feeder.feed(data),
                          fetch_list=[avg_loss, acc])
        if step % 10 == 0: #每训练100次 打印一次log
            event_handler_plot(train_prompt, step, metrics[0])
        step += 1

    # 测试每个epoch的分类效果
    avg_loss_val, acc_val = train_test(train_test_program=test_program,
                                      train_test_reader=test_reader,
                                      train_test_feed=feeder)

    event_handler_plot(test_prompt, step, metrics[0])

    lists.append((epoch_id, avg_loss_val, acc_val))

    # 保存训练好的模型参数用于预测
    if save_dirname is not None:
        fluid.io.save_inference_model(save_dirname,
                                      ["img"], [prediction], exe,
                                      model_filename=None,
                                      params_filename=None)

# 选择效果最好的pass
best = sorted(lists, key=lambda list: float(list[1]))[0]
print('Best pass is %s, testing Avgcost is %s' % (best[0], best[1]))
print('The classification accuracy is %.2f%%' % (float(best[2]) * 100))

После обучения проверьте точность предсказания модели. При обучении с помощью MNIST точность классификации общей модели регрессии softmax составляет около 92,34%, многослойного персептрона — 97,66%, а сверточной нейронной сети может достигать 99,20%.

Примечание. В плотере и печати в aistudio есть встроенные ошибки, и плоттер можно использовать только после отмены печати.

прикладная модель

Изображения рукописных цифр можно классифицировать с помощью обученной модели.Следующая программа показывает, как использовать обученную модель для вывода.

Генерация входных данных прогнозирования

infer_3.pngпример изображения числа 3. Превратите его в массив numpy, чтобы он соответствовал формату потока данных.

def load_image(file):
    # 读取图片文件,并将它转成灰度图
    im = Image.open(file).convert('L')
    # 将输入图片调整为 28*28 的高质量图
    im = im.resize((28, 28), Image.ANTIALIAS)
    # 将图片转换为numpy
    im = numpy.array(im).reshape(1, 1, 28, 28).astype(numpy.float32)
    # 对数据作归一化处理
    im = im / 255.0 * 2.0 - 1.0
    return im

tensor_img = load_image('work/infer_3.png')

Создание вывода и предсказание

пройти черезload_inference_modelнастроить сеть и обученные параметры. Мы можем просто подключить классификатор, который мы определили ранее.

inference_scope = fluid.core.Scope()
with fluid.scope_guard(inference_scope):
    # 使用 fluid.io.load_inference_model 获取 inference program desc,
    # feed_target_names 用于指定需要传入网络的变量名
    # fetch_targets 指定希望从网络中fetch出的变量名
    [inference_program, feed_target_names,
     fetch_targets] = fluid.io.load_inference_model(
     save_dirname, exe, None, None)

    # 将feed构建成字典 {feed_target_name: feed_target_data}
    # 结果将包含一个与fetch_targets对应的数据列表
    results = exe.run(inference_program,
                            feed={feed_target_names[0]: tensor_img},
                            fetch_list=fetch_targets)
    lab = numpy.argsort(results)

    # 打印 infer_3.png 这张图片的预测结果
    img=Image.open('work/infer_3.png')
    plt.imshow(img)
    print("Inference result of work/infer_3.png is: %d" % lab[0][0][-1])

Обратите внимание на публичный аккаунт WeChat: Lao Qi Classroom. Читайте подробные статьи, получайте превосходные навыки и наслаждайтесь блестящей жизнью.

WechatIMG6