【手把手带你入门深度学习之150行代码的汉字识别系统】学习笔记 ·002 训练神经网络

公孙茂学
2023-12-01

立即学习:手把手带你入门深度学习之150行代码的汉字识别系统-5-训练神经网络-李梓佳的在线视频教程-CSDN程序员研修院

目录

 一、神经网络训练代码

二、思路总结

1、数据集图片数据、目标值的导入

2、目标值转化为one_hot编码

3、神经网络中样本、目标的输入

4、神经网络的搭建

5、损失的计算与优化

6、批处理的方法

7、准确率的计算

8、模型的保存

三、API总结


一、神经网络训练代码

import tensorflow as tf
import cv2
import glob
import os
import numpy as np


def data_reader(dataset_path):
    data_list = []
    label_list = []
    for cls_path in glob.glob(os.path.join(dataset_path, '*')):
        for file_name in glob.glob((os.path.join(cls_path, '*'))):
            img = cv2.imread(file_name)
            data_list.append(img)
            label_list.append(int(cls_path[-1]))
    data_np = np.array(data_list)
    label_np = np.array(label_list)
    return data_np, label_np


def shuffle_data(data, label):
    idx = np.arange(len(data))
    np.random.shuffle(idx)
    return data[idx, ...], label[idx, ...]


def train(data, label):
    data_in = tf.placeholder(tf.float32, [None, 100, 100, 3], name="data_in")  # None实际上指的是batch的大小,batch的大小可以在运行时改变
    label_in = tf.placeholder(tf.float32, [None, 3])  # 准备构造一个one_hot的label,而我们的数据总共有3个类

    # 一个小的卷积网络,用来处理图片
    # (如果数据量大,需要构造一个大一点的神经网络,可以在卷积网络部分复制一下,就可以构造一个比较深的神经网络)
    # (但是神经网络并不是越深越好,可能会涉及过拟合、梯度消失等问题)
    # (目前网络参数的取值,主要还是根据数据集特征和大小,还有靠我们的直觉的经验。)
    # (所以神经网络也叫作当代炼金术)
    out = tf.layers.conv2d(data_in, 4, 3, padding='same')  # out形状为[?, 100, 100, 4]
    out = tf.layers.max_pooling2d(out, 2, 2, padding='same')  # out形状为[?, 50, 50, 4]
    out = tf.nn.relu(out)  # out形状为[?, 50, 50, 4]

    # 把提取出来的特征压扁成一个一维数组
    out = tf.reshape(out, (-1, int(np.prod(out.get_shape()[1:]))))  # out形状为[?, 10000]

    # 送入全连接层
    out = tf.layers.dense(out, 2000, activation=tf.nn.relu)
    # 再加一个全连接层(按理说,我们数据量很少,应该会过拟合,也就数准确率接近100%)
    out = tf.layers.dense(out, 256, activation=tf.nn.relu)

    # 得到输出(输出的分类数要和输入的label的分类数一样,不然会报错)
    pred = tf.layers.dense(out, 3)

    # 将one_hot变回值0、1、2
    out_label = tf.argmax(pred, 1, name="output")

    # 计算交叉熵损失
    loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))
    # 梯度下降、优化损失
    train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

    # 初始化变量的op
    init_op = tf.initialize_all_variables()

    # 定义一些参数
    batch_size = 16

    # 进行会话
    with tf.Session() as sess:
        sess.run(init_op)
        # 进行多次训练
        for epoch in range(50):
            # (在训练的时候,我们不希望每次取到的数据的顺序都是一样的,这样很容易导致过拟合)
            # (因此我们一般会加一个shuffle,也就是在输入到网络前,把数据顺序打乱)
            datas, labels = shuffle_data(data, label)
            # 准确率统计
            total_loss = 0
            avg_accuracy = 0
            # 按批次训练
            num_batch = len(data) // batch_size
            for batch_idx in range(num_batch):
                # 计算每个batch开始和结束时候的下标
                start_idx = batch_idx * batch_size
                end_idx = (batch_idx + 1) * batch_size
                # 准备好输入层的数据
                # (...代表省略后面的形状)
                feed_dict = {data_in: datas[start_idx: end_idx, ...],
                             label_in: labels[start_idx: end_idx, ...]}
                # 计算准确率
                correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1))
                accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
                # 进行训练
                _, loss_out, acc = sess.run([train_op, loss, accuracy], feed_dict=feed_dict)
                # 准确率统计
                total_loss += loss_out
                avg_accuracy += acc
            # 统计每批次准确率
            print("avg_accuracy", avg_accuracy / num_batch)
            # 准确率够高则保存模型
            if avg_accuracy / num_batch > 0.94:
                saver = tf.train.Saver()
                saver.save(sess, './model/model')
                break


def dense_to_one_hot(label, num_class):
    num_label = label.shape[0]
    index_offset = np.arange(num_label) * num_class
    label_one_hot = np.zeros((num_label, num_class))
    label_one_hot.flat[index_offset + label.ravel()] = 1
    return label_one_hot


if __name__ == '__main__':
    dataset_path = "./dataset"
    data, label = data_reader(dataset_path)
    one_hot_label = dense_to_one_hot(label, 3)
    train(data, one_hot_label)

二、思路总结

1、数据集图片数据、目标值的导入

图片数据

首先,用cv读取图片,将图片一个一个添加到一个list里面去。

其次,用np(numpy)将图片的列表转化为np数组。

图片目标值

首先,由于图片被按照目录分类

在读取图片时,每读取一张图,就将图片的上级目录的名字作为一个目标值。(这里是0、1、2)

2、目标值转化为one_hot编码

步骤如下:

  1. 原本一个样本只有一个目标值

  2. 一共600个样本,目标值总的可能性有3种(某种目标的one_hot编码就只可能是[1, 0, 0]、[0, 1, 0]、[0, 0, 1])

  3. 先生成好一个全零的、形状为(600, 3)的np数组퐴,准备将值1填入对应标签位置的位置

  4. 准备好每个样本在퐴展平后的数组퐵中的one_hot特征下标

  5. 将1填入上一步中展平下标对应在数组퐴中的位置

3、神经网络中样本、目标的输入

可以通过一个占位符来实现,个数的那一维填写None,代表暂时不知道每次训练的样本数量,在实际训练中会根据每批次训练数量加以给定。

比如,我们这里的样本数为600,每个样本的形状都是(100, 100, 3),也就是总的样本的形状为(600, 100, 100, 3),那么我们样本输入的占位符的形状就应该为(None, 100, 100, 3);同样地,我们one_hot目标值的形状为(600, 3),那么我们样本目标值的占位符形状就应该为(None, 3)

4、神经网络的搭建

步骤如下:

  1. 接入一个卷积层(tf.layers.conv2d)

    第一个参数(inputs)为输入数据

    第二个参数(filters)为卷积核的数量

    第三个参数(kernel_size)为卷积核的大小

    (这里因为我们的数据比较简单、类别也少,所以filters取了比较小的值4)

    (如果像是imagenet那种大型数据集一般会取到128或者256那样)

  2. 接入一个池化层(用于减少数据量)(tf.layers.max_pooling2d)

    第一个参数(inputs)为输入

    第二个参数(pool_size)为池化核的大小

    第三个参数(strides)为卷积核的步长

    (这里我们都把pool_size和strides设置为2)

  3. 加上一个激活函数ReLU

  4. 把神经网络的输出拍扁,做成一个一维数组

  5. 添加几个全连接层(tf.layers.dense)

    第一个参数(inputs)为输入

    第二个参数(unit)为输出的维度大小(改变inputs的最后一维),同时也是神经元的个数

    (一开始中间加入的是一层,后来发现一层全连接层训练的效果不怎么好,改成了两层)

  6. 再接入一个全连接层得到输出

    (注意,输出的分类数要和目标label的分类数相同,否则会报错)

5、损失的计算与优化

损失计算

计算交叉熵损失

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))

优化

使用Adam优化算法

train_op = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

6、批处理的方法

步骤如下:

  1. 设置每批次的训练数量batch_size、训练的趟数

  2. 每趟训练前件训练用的输入数据随机打乱

  3. 计算好每一趟训练的批次数量,对每一批次用batch_size个样本进行训练

7、准确率的计算

步骤如下:

  1. 找到预测值、目标值的每一个样本在one_hot编码中的横向下标

    tf.argmax(pred, 1, name="output")
    tf.argmax(label_in, 1)
  2. 对比预测值、目标值的下标值是否相等,由此得到一个判断对应样本的预测值与目标值是否相等的tf张量

    correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(label_in, 1))
  3. 从上面的比对张量中计算平均值,得到这一批次的准确率

  4. 在一趟训练结束后,将所有批次准确率相加求和,再除以训练批次数量,就得到了这一趟训练的平均准确率

8、模型的保存

步骤如下:

  1. 构造一个tf模型存储器

    saver = tf.train.Saver()
  2. 指定会话、模型存储路径

    saver.save(sess, './目录/模型名')

这里需要注意的是,我们需要为输入数据占位符data_in、预测值out_label增加上各自的name,这是为了之后模型读取的方便

三、API总结

API

作用

使用示例

glob.glob

返回某个路径下所有被匹配的文件/目录路径生成的列表

for cls_path in glob.glob(os.path.join(dataset_path, '*')):

        ......

np.array

根据某种数据类型的值生成相应的np数组

data_np = np.array(data_list)

np.arange

按照指定的start、stop、step生成一个指定的np一维等差数组

index_offset = np.arange(num_label) * num_class

np.zeros

按照指定的shape生成元素全为0的np数组

label_one_hot = np.zeros((num_label, num_class))

*.flat[???]

根据数组展平后的下标获取数组元素

label_one_hot.flat[index_offset + label.ravel()] = 1

np.random.shuffle

随机打乱一串数

np.random.shuffle(idx)

tf.placeholder

创建一个tf占位符

label_in = tf.placeholder(tf.float32, [None, 3])

tf.Session

创建一个tf会话

with tf.Session() as sess:

        ......

tf.initialize_all_variables

创建一个tf变量初始化op

init_op = tf.initialize_all_variables()

tf.train.Saver

创建一个tf会话存储器

saver = tf.train.Saver()

*.save

保存会话中的训练模型

saver.save(sess, './model/model')

tf.argmax

找出张量在某一维度上的最大值对应的下标

out_label = tf.argmax(pred, 1, name="output")

tf.reduce_mean

计算平均值

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

tf.cast

将某个张量的数据类型进行转换

accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

tf.equal

按元素比较两个张量,返回相等情况对应的张量

correct_prediction = tf.equal(out_label, tf.argmax(label_in, 1))

tf.nn.

softmax_cross_entropy

_with_logits_v2

计算交叉熵损失

loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=label_in, logits=pred))

tf.layers.conv2d

构建卷积层

out = tf.layers.conv2d(data_in, 4, 3, padding='same')

tf.layers.max_pooling2d

构建池化层

out = tf.layers.max_pooling2d(out, 2, 2, padding='same')

tf.nn.relu

激活函数ReLU

out = tf.nn.relu(out)

tf.layers.dense

构造全连接层

out = tf.layers.dense(out, 2000, activation=tf.nn.relu)

 类似资料: