<jupyter_start><jupyter_text>请点击[此处](https://ai.baidu.com/docs/AIStudio_Project_Notebook/a38e5576)查看本环境基本用法.  Please click [here ](https://ai.baidu.com/docs/AIStudio_Project_Notebook/a38e5576) for more detailed instructions.   一、项目背景介绍 1. 项目背景及意义近几年来,在我国交通运输网络快速发展的环境背景下,全国汽车保有量出现井喷式的增长。随着道路车辆越来越多，人们越来越重视道路交通中的行驶安全。当下自动辅助驾驶已成为智能化交通的研究热点，在智能化的交通背景下交通标志识别系统是自动辅助驾驶重要的一部分。在现实环境道路上进行自动驾驶,意味着采用的交通标志识别系统必须能够快速准确的捕捉到交通标志。然而实际道路的环境因素非常复杂，如何准确、高效地识别交通标志，是交通标志识别系统研究的重点。 2. 国内外研究现状 2.1 国外研究现状国外对交通标志的研究起步较早，且发展速度非常迅速。早在上世纪80年代，日本就开始了相应的研究工作。从90年代开始许多欧洲国家也加入到了相应的研究。在理论方面国外对交通标志的检测从理论基础和可行性分析方面已做了大量研究。其中主要采用基于颜色和形状的方法对交通标志进行分割提取和形状分析。1987年，日本率先开启了智能交通系统研究的大门，主要采用阈值分割、模版匹配等一系列经典的方法对限速标志进行识别，所需时间约为0.516秒，但未给出具体的检测识别准确率。1992年，法国的Saint-Blancard对带红色的标志进行研究，采用红色滤波器边缘及闭合曲线方法获取目标。其针对小规模样本进行实验，平均识别率高达94.9%。1993年，美国开发了ADIS系统，它的明军分辨率为75%，但尚不能用于检测。1994年，意大利热那亚大学的Piccioli等人根据交通标志不同的几何形状进行检测，该方法对不同形状的标志检测准确率不同，三角形标志可达92%，圆形可达94%左右。1998年起，意大利帕尔马大学采用颜色分割、形状约束、径向基函数RBF分类器进行检测识别，准确率可达90%以上。  进入21世纪，随着图像处理技术和计算机视觉理论的不断发展，交通标志检测的方法变的多样化，无论是实效性还是准确性都有所提高。2004年，澳大利亚的<NAME>自动化研究所和瑞典的<NAME>实验室合作研发了一套交通标志识别系统，识别率可达95%，但仍难以满足实时性要求。2007年，Won等人引入人类视觉选择注意力机制进行交通标志检测，该方法趋于完美，识别率也高达94.52%。但该方法仍有一些局限性。  应用方面，交通标志检测的最终目标是在实际的智能车辆导航系统中得到广泛的应用。如今，许多科技发达的西方国家这方面有了很大的成就，而且已经有不少技术开始投入使用。 2.2 国内研究现状国内对于智能研究系统的起步较晚，在交通标志检测方面的研究较少，目前仍处于探索性研究阶段，虽已在研究模型实验车，但尚未形成完整的实际应用系统。  理论方面，1998年蒋刚毅等人利用数学形态学方法、骨架函数为特征的模版匹配对警告标志进行检测，初步实现了位移不变性和鲁棒性。随后许多研究机构和大学也进行了研究，并在理论方面已取得了一定的成果。有学者将多种方法进行结合来提高检测效率，如浙江大学的何耀平等人将Adaboost算法与支持向量机融合用于交通标志的识别中。除此之外，基于视觉显著性的方法也逐渐被国内学者使用，如尹涛采用自顶向下和自底向上相结合的视觉视觉显著性检测算法实现了交通标志检测定位，但是这种方法计算速度较慢，难以完成实施检测。  应用方面，随着科技的发展和人类生活水平的提高，我国对智能汽车的投入越来越多，同时交通标志的识别系统也受到了广大学者的关注，近几年也取得了一批重要的科技成果。  二、数据介绍本项目采用[中国交通标志](https://aistudio.baidu.com/aistudio/datasetdetail/107275/0)数据集进行算法设计和测试。   1. 图片数据该数据集源自中国交通标志识别数据库。里加数据科学俱乐部成员已经探索了它，以便对卷积神经网络进行一些培训。  数据集由 58 个类别的 5998 张交通标志图像组成。每个图像都是单个交通标志的放大视图。（例如，限速 5 公里/小时）  ![限速标志.png](attachment:17582e98-2c3a-41e9-9739-977c6ea7d9be.png)   2. 注释文档注释提供图像属性（文件名、宽度、高度）以及图像和类别中的交通标志坐标。  注释说明：  annotations.csv：  * file_name：包含交通标志的图像的文件名  * width：图片宽度  * height：图片高度  * x1：边界矩形左上角X坐标  * y1：边界矩形左上角Y坐标  * x2：边界矩形右下角X坐标  * y2：边界矩形右下角Y坐标  * category：交通标志类别  *（随机图片展示在本节最后代码框）*<jupyter_code># 解压交通标志数据集及预训练参数

# 交通标志数据集
!cd data/data107275 && unzip -oq archive\(5\).zip
# 预训练参数
!cd data/data6487 && unzip -oq ResNet50_pretrained.zip
# 简单处理数据
# 生产两者的标签文件以及所有图片的RGB得均值与均方差
import codecs
import os
import random
import shutil
import pandas as pd
import numpy as np
import cv2
import glob
from PIL import Image


def get_mean_std(image_path_list):
    print('Total images:', len(image_path_list))
    max_val, min_val = np.zeros(3), np.ones(3) * 255
    mean, std = np.zeros(3), np.zeros(3)
    for image_path in image_path_list:
        image = cv2.imread(image_path)
        for c in range(3):
            mean[c] += image[:, :, c].mean()
            std[c] += image[:, :, c].std()
            max_val[c] = max(max_val[c], image[:, :, c].max())
            min_val[c] = min(min_val[c], image[:, :, c].min())

    mean /= len(image_path_list)
    std /= len(image_path_list)

    mean /= max_val - min_val
    std /= max_val - min_val

    return mean, std


mean,std=get_mean_std(glob.glob('data/data107275/images/*.png'))
print('mean={}  std={}'.format(mean,std))


df = pd.read_csv('data/data107275/annotations.csv')
df = df.drop_duplicates(subset='file_name', keep='last', inplace=False).reset_index(drop=True) # 删除注释中重复值
print(df.shape)

for path in range(1+df['category'].max()):
    if not os.path.exists('data/data107275/images/'+str(path)):
        os.makedirs('data/data107275/images/'+str(path))

for i in range(df.shape[0]):
    
    shutil.move('data/data107275/images/'+str(df['file_name'][i]), 'data/data107275/images/'+str(df['category'][i]))
# 对本地数据做一些预处理，主要用于随机分组，一部分用于训练，一部分用于验证。
import codecs
import os
import random
import shutil
import pandas as pd
from PIL import Image

train_ratio = 4.0 / 5

all_file_dir = 'data/data107275/images/'
class_list = [c for c in os.listdir(all_file_dir) if os.path.isdir(os.path.join(all_file_dir, c)) and not c.endswith('Set') and not c.startswith('.')]
class_list.sort()
print(class_list)
train_image_dir = os.path.join(all_file_dir, "trainImageSet")
if not os.path.exists(train_image_dir):
    os.makedirs(train_image_dir)
    
eval_image_dir = os.path.join(all_file_dir, "evalImageSet")
if not os.path.exists(eval_image_dir):
    os.makedirs(eval_image_dir)

train_file = codecs.open(os.path.join(all_file_dir, "train.txt"), 'w')
eval_file = codecs.open(os.path.join(all_file_dir, "eval.txt"), 'w')

def picCrop(image_path):
    img_info=df.loc[df['file_name']==image_path.split('/')[-1]]
    box=(img_info['x1'],img_info['y1'],img_info['x2'],img_info['y2'])
    region = img.crop(box) 
    region.save(image_path)


df = pd.read_csv('data/data107275/annotations.csv')
df = df.drop_duplicates(subset='file_name', keep='last', inplace=False).reset_index(drop=True) # 删除注释中重复值

with codecs.open(os.path.join(all_file_dir, "label_list.txt"), "w") as label_list:
    for class_dir in class_list:
        label_list.write("{0}\t{1}\n".format(class_dir, class_dir))
        image_path_pre = os.path.join(all_file_dir, class_dir)
        for file in os.listdir(image_path_pre):
            try:
                img = Image.open(os.path.join(image_path_pre, file))
                if random.uniform(0, 1) <= train_ratio:
                    # picCrop(os.path.join(image_path_pre, file))
                    shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(train_image_dir, file))
                    train_file.write("{0}\t{1}\n".format(os.path.join(train_image_dir, file), class_dir))
                else:
                    # picCrop(os.path.join(image_path_pre, file))
                    shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(eval_image_dir, file))
                    eval_file.write("{0}\t{1}\n".format(os.path.join(eval_image_dir, file), class_dir))
            except Exception as e:
                pass
                # 存在一些文件打不开，此处需要稍作清洗
            
train_file.close()
eval_file.close()
# 随机展示数据集中的图片
from PIL import Image
import random
import matplotlib.pyplot as plt

img_path='./data/data107275/images/trainImageSet/'
for root, dirs, files in os.walk(img_path, topdown=False):
    img_path_list = files
img=Image.open(img_path+img_path_list[random.randint(0,len(img_path_list))])
plt.imshow(img)
plt.axis('off')
plt.show()<jupyter_output><empty_output><jupyter_text>三、模型介绍ResNet(Residual Network)是2015年ImageNet图像分类、图像物体定位和图像物体检测比赛的冠军。针对随着网络训练加深导致准确度下降的问题，ResNet提出了残差学习方法来减轻训练深层网络的困难。在已有设计思路(BN, 小卷积核，全卷积网络)的基础上，引入了残差模块。每个残差模块包含两条路径，其中一条路径是输入特征的直连通路，另一条路径对该特征做两到三次卷积操作得到该特征的残差，最后再将两条路径上的特征相加。  残差模块如图1所示，左边是基本模块连接方式，由两个输出通道数相同的3x3卷积组成。右边是瓶颈模块(Bottleneck)连接方式，之所以称为瓶颈，是因为上面的1x1卷积用来降维(图示例即256->64)，下面的1x1卷积用来升维(图示例即64->256)，这样中间3x3卷积的输入和输出通道数都较小(图示例即64->64)。![resnet_block.jpg](attachment:e72aeb61-abbe-4d1e-a0b5-5a91dfbcee56.jpg)图1. 残差模块  图2展示了50、101、152层网络连接示意图，使用的是瓶颈模块。这三个模型的区别在于每组中残差模块的重复次数不同(见图右上角)。ResNet训练收敛较快，成功的训练了上百乃至近千层的卷积神经网络。![resnet.png](attachment:982df35c-c05e-4904-b726-87b114e041c1.png)图2. 基于ImageNet的ResNet模型  ResNet解读博客https://blog.csdn.net/lanran2/article/details/79057994  想写好一个好的精品项目，项目的内容必须包含理论内容和实践相互结合，该部分主要是理论部分，向大家介绍一下你的模型原理等内容。<jupyter_code># ResNet 模型网络结构
class ResNet(object):
    """
    resnet的网络结构类
    """

    def __init__(self, layers=50):
        """
        resnet的网络构造函数
        :param layers: 网络层数
        """
        self.layers = layers

    def name(self):
        """
        获取网络结构名字
        :return:
        """
        return 'resnet'

    def net(self, input, class_dim=1000):
        """
        构建网络结构
        :param input: 输入图片
        :param class_dim: 分类类别
        :return:
        """
        layers = self.layers
        supported_layers = [50, 101, 152]
        assert layers in supported_layers, \
            "supported layers are {} but input layer is {}".format(supported_layers, layers)

        if layers == 50:
            depth = [3, 4, 6, 3]
        elif layers == 101:
            depth = [3, 4, 23, 3]
        elif layers == 152:
            depth = [3, 8, 36, 3]
        num_filters = [64, 128, 256, 512]

        conv = self.conv_bn_layer(
            input=input,
            num_filters=64,
            filter_size=7,
            stride=2,
            act='relu',
            name="conv1")
        conv = fluid.layers.pool2d(
            input=conv,
            pool_size=3,
            pool_stride=2,
            pool_padding=1,
            pool_type='max')

        for block in range(len(depth)):
            for i in range(depth[block]):
                if layers in [101, 152] and block == 2:
                    if i == 0:
                        conv_name = "res" + str(block + 2) + "a"
                    else:
                        conv_name = "res" + str(block + 2) + "b" + str(i)
                else:
                    conv_name = "res" + str(block + 2) + chr(97 + i)
                conv = self.bottleneck_block(
                    input=conv,
                    num_filters=num_filters[block],
                    stride=2 if i == 0 and block != 0 else 1,
                    name=conv_name)

        pool = fluid.layers.pool2d(input=conv, pool_size=7, pool_type='avg', global_pooling=True)
        stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0)
        out = fluid.layers.fc(input=pool,
                              size=class_dim,
                              act='softmax',
                              param_attr=fluid.param_attr.ParamAttr(initializer=Uniform(-stdv, stdv)))
        return out

    def conv_bn_layer(self,
                      input,
                      num_filters,
                      filter_size,
                      stride=1,
                      groups=1,
                      act=None,
                      name=None):
        """
        便捷型卷积结构，包含了batch_normal处理
        :param input: 输入图片
        :param num_filters: 卷积核个数
        :param filter_size: 卷积核大小
        :param stride: 平移
        :param groups: 分组
        :param act: 激活函数
        :param name: 卷积层名字
        :return:
        """
        conv = fluid.layers.conv2d(
            input=input,
            num_filters=num_filters,
            filter_size=filter_size,
            stride=stride,
            padding=(filter_size - 1) // 2,
            groups=groups,
            act=None,
            param_attr=ParamAttr(name=name + "_weights"),
            bias_attr=False,
            name=name + '.conv2d.output.1')
        if name == "conv1":
            bn_name = "bn_" + name
        else:
            bn_name = "bn" + name[3:]
        return fluid.layers.batch_norm(
            input=conv,
            act=act,
            name=bn_name + '.output.1',
            param_attr=ParamAttr(name=bn_name + '_scale'),
            bias_attr=ParamAttr(bn_name + '_offset'),
            moving_mean_name=bn_name + '_mean',
            moving_variance_name=bn_name + '_variance', )

    def shortcut(self, input, ch_out, stride, name):
        """
        转换结构，转换输入和输出一致，方便最后的短链接结构
        :param input:
        :param ch_out:
        :param stride:
        :param name:
        :return:
        """
        ch_in = input.shape[1]
        if ch_in != ch_out or stride != 1:
            return self.conv_bn_layer(input, ch_out, 1, stride, name=name)
        else:
            return input

    def bottleneck_block(self, input, num_filters, stride, name):
        """
        resnet的短路链接结构中的一种，采用压缩方式先降维，卷积后再升维
        利用转换结构将输入变成瓶颈卷积一样的尺寸，最后将两者按照位相加，完成短路链接
        :param input:
        :param num_filters:
        :param stride:
        :param name:
        :return:
        """
        conv0 = self.conv_bn_layer(
            input=input,
            num_filters=num_filters,
            filter_size=1,
            act='relu',
            name=name + "_branch2a")
        conv1 = self.conv_bn_layer(
            input=conv0,
            num_filters=num_filters,
            filter_size=3,
            stride=stride,
            act='relu',
            name=name + "_branch2b")
        conv2 = self.conv_bn_layer(
            input=conv1,
            num_filters=num_filters * 4,
            filter_size=1,
            act=None,
            name=name + "_branch2c")

        short = self.shortcut(
            input, num_filters * 4, stride, name=name + "_branch1")

        return fluid.layers.elementwise_add(
            x=short, y=conv2, act='relu', name=name + ".add.output.5")<jupyter_output><empty_output><jupyter_text>四、模型训练该部分主要是实践部分，也是相对来说话费时间最长的一部分，该部分主要展示模型训练的内容，同时向大家讲解模型参数的设置  1.训练配置* 训练轮数* 每批次训练图片数量* 是否使用GPU训练* 学习率调整* 训练图片尺寸<jupyter_code>"""
训练常用视觉基础网络，用于分类任务
需要将训练图片，类别文件 label_list.txt 放置在同一个文件夹下
程序会先读取 train.txt 文件获取类别数和图片数量
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import numpy as np
import time
import math
import paddle
import paddle.fluid as fluid
import codecs
import logging

from paddle.fluid.initializer import MSRA
from paddle.fluid.initializer import Uniform
from paddle.fluid.param_attr import ParamAttr
from PIL import Image
from PIL import ImageEnhance


train_parameters = {
    "input_size": [3, 224, 224],
    "class_dim": -1,  # 分类数，会在初始化自定义 reader 的时候获得
    "image_count": -1,  # 训练图片数量，会在初始化自定义 reader 的时候获得
    "label_dict": {},
    "data_dir": "data/data107275/images/",  # 训练数据存储地址
    "train_file_list": "train.txt",
    "label_file": "label_list.txt",
    "save_freeze_dir": "./freeze-model",
    "save_persistable_dir": "./persistable-params",
    "continue_train": True,        # 是否接着上一次保存的参数接着训练，优先级高于预训练模型
    "pretrained": True,            # 是否使用预训练的模型
    "pretrained_dir": "data/data6487/ResNet50_pretrained", 
    "mode": "train",
    "num_epochs": 100,
    "train_batch_size": 128,
    "mean_rgb": mean,  # 常用图片的三通道均值，通常来说需要先对训练数据做统计，此处仅取中间值
    "use_gpu": True,
    "image_enhance_strategy": {  # 图像增强相关策略
        "need_distort": True,  # 是否启用图像颜色增强
        "need_rotate": True,   # 是否需要增加随机角度
        "need_crop": True,      # 是否要增加裁剪
        "need_flip": True,      # 是否要增加水平随机翻转
        "hue_prob": 0.5,
        "hue_delta": 18,
        "contrast_prob": 0.5,
        "contrast_delta": 0.5,
        "saturation_prob": 0.5,
        "saturation_delta": 0.5,
        "brightness_prob": 0.5,
        "brightness_delta": 0.125
    },
    "early_stop": {
        "sample_frequency": 50,
        "successive_limit": 10,
        "good_acc1": 0.90
    },
    "rsm_strategy": {
        "learning_rate": 0.002,
        "lr_epochs": [100, 200, 400, 800, 1200, 2000, 3000],
        "lr_decay": [1, 0.5, 0.25, 0.1, 0.05, 0.01, 0.005, 0.002]
    },
    "momentum_strategy": {
        "learning_rate": 0.002,
        "lr_epochs": [100, 200, 400, 800, 1200, 2000, 3000],
        "lr_decay": [1, 0.5, 0.25, 0.1, 0.05, 0.01, 0.005, 0.002]
    },
    "sgd_strategy": {
        "learning_rate": 0.002,
        "lr_epochs": [100, 200, 400, 800, 1200, 2000, 3000],
        "lr_decay": [1, 0.5, 0.25, 0.1, 0.05, 0.01, 0.005, 0.002]
    },
    "adam_strategy": {
        "learning_rate": 0.002
    }
}


def init_train_parameters():
    """
    初始化训练参数，主要是初始化图片数量，类别数
    :return:
    """
    train_file_list = os.path.join(train_parameters['data_dir'], train_parameters['train_file_list'])
    label_list = os.path.join(train_parameters['data_dir'], train_parameters['label_file'])
    index = 0
    print(label_list)
    print(train_file_list)
    with codecs.open(label_list, encoding='utf-8') as flist:
        lines = [line.strip() for line in flist]
        for line in lines:
            parts = line.strip().split()
            train_parameters['label_dict'][parts[1]] = int(parts[0])
            index += 1
        train_parameters['class_dim'] = index
    with codecs.open(train_file_list, encoding='utf-8') as flist:
        lines = [line.strip() for line in flist]
        train_parameters['image_count'] = len(lines)<jupyter_output><empty_output><jupyter_text>2. 日志输出配置输出模型训练每阶段的日志信息<jupyter_code>def init_log_config():
    """
    初始化日志相关配置
    :return:
    """
    global logger
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    log_path = os.path.join(os.getcwd(), 'logs')
    if not os.path.exists(log_path):
        os.makedirs(log_path)
    log_name = os.path.join(log_path, 'train.log')
    sh = logging.StreamHandler()
    fh = logging.FileHandler(log_name, mode='w')
    fh.setLevel(logging.DEBUG)
    formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
    fh.setFormatter(formatter)
    sh.setFormatter(formatter)
    logger.addHandler(sh)
    logger.addHandler(fh)<jupyter_output><empty_output><jupyter_text>3. 图像增强* 图片尺寸归一* 随机裁剪* 随机选装角度* 随机调整亮度* 随机调整对比度* 随机调整饱和度* 随机调整色度<jupyter_code>def resize_img(img, target_size):
    """
    强制缩放图片
    :param img:
    :param target_size:
    :return:
    """
    target_size = input_size
    img = img.resize((target_size[1], target_size[2]), Image.BILINEAR)
    return img


def random_crop(img, scale=[0.08, 1.0], ratio=[3. / 4., 4. / 3.]):
    aspect_ratio = math.sqrt(np.random.uniform(*ratio))
    w = 1. * aspect_ratio
    h = 1. / aspect_ratio

    bound = min((float(img.size[0]) / img.size[1]) / (w**2),
                (float(img.size[1]) / img.size[0]) / (h**2))
    scale_max = min(scale[1], bound)
    scale_min = min(scale[0], bound)

    target_area = img.size[0] * img.size[1] * np.random.uniform(scale_min,
                                                                scale_max)
    target_size = math.sqrt(target_area)
    w = int(target_size * w)
    h = int(target_size * h)

    i = np.random.randint(0, img.size[0] - w + 1)
    j = np.random.randint(0, img.size[1] - h + 1)

    img = img.crop((i, j, i + w, j + h))
    img = img.resize((train_parameters['input_size'][1], train_parameters['input_size'][2]), Image.BILINEAR)
    return img


def rotate_image(img):
    """
    图像增强，增加随机旋转角度
    """
    angle = np.random.randint(-14, 15)
    img = img.rotate(angle)
    return img


def random_brightness(img):
    """
    图像增强，亮度调整
    :param img:
    :return:
    """
    prob = np.random.uniform(0, 1)
    if prob < train_parameters['image_enhance_strategy']['brightness_prob']:
        brightness_delta = train_parameters['image_enhance_strategy']['brightness_delta']
        delta = np.random.uniform(-brightness_delta, brightness_delta) + 1
        img = ImageEnhance.Brightness(img).enhance(delta)
    return img


def random_contrast(img):
    """
    图像增强，对比度调整
    :param img:
    :return:
    """
    prob = np.random.uniform(0, 1)
    if prob < train_parameters['image_enhance_strategy']['contrast_prob']:
        contrast_delta = train_parameters['image_enhance_strategy']['contrast_delta']
        delta = np.random.uniform(-contrast_delta, contrast_delta) + 1
        img = ImageEnhance.Contrast(img).enhance(delta)
    return img


def random_saturation(img):
    """
    图像增强，饱和度调整
    :param img:
    :return:
    """
    prob = np.random.uniform(0, 1)
    if prob < train_parameters['image_enhance_strategy']['saturation_prob']:
        saturation_delta = train_parameters['image_enhance_strategy']['saturation_delta']
        delta = np.random.uniform(-saturation_delta, saturation_delta) + 1
        img = ImageEnhance.Color(img).enhance(delta)
    return img


def random_hue(img):
    """
    图像增强，色度调整
    :param img:
    :return:
    """
    prob = np.random.uniform(0, 1)
    if prob < train_parameters['image_enhance_strategy']['hue_prob']:
        hue_delta = train_parameters['image_enhance_strategy']['hue_delta']
        delta = np.random.uniform(-hue_delta, hue_delta)
        img_hsv = np.array(img.convert('HSV'))
        img_hsv[:, :, 0] = img_hsv[:, :, 0] + delta
        img = Image.fromarray(img_hsv, mode='HSV').convert('RGB')
    return img


def distort_color(img):
    """
    概率的图像增强
    :param img:
    :return:
    """
    prob = np.random.uniform(0, 1)
    # Apply different distort order
    if prob < 0.35:
        img = random_brightness(img)
        img = random_contrast(img)
        img = random_saturation(img)
        img = random_hue(img)
    elif prob < 0.7:
        img = random_brightness(img)
        img = random_saturation(img)
        img = random_hue(img)
        img = random_contrast(img)
    return img<jupyter_output><empty_output><jupyter_text>4. 自定义数据读取器读取图片数据进行训练及测试<jupyter_code>def custom_image_reader(file_list, data_dir, mode):
    """
    自定义用户图片读取器，先初始化图片种类，数量
    :param file_list:
    :param data_dir:
    :param mode:
    :return:
    """
    with codecs.open(file_list) as flist:
        lines = [line.strip() for line in flist]

    def reader():
        np.random.shuffle(lines)
        for line in lines:
            if mode == 'train' or mode == 'val':
                img_path, label = line.split()
                img = Image.open(img_path)
                try:
                    if img.mode != 'RGB':
                        img = img.convert('RGB')
                    if train_parameters['image_enhance_strategy']['need_distort'] == True:
                        img = distort_color(img)
                    if train_parameters['image_enhance_strategy']['need_rotate'] == True:
                        img = rotate_image(img)
                    if train_parameters['image_enhance_strategy']['need_crop'] == True:
                        img = random_crop(img, train_parameters['input_size'])
                    if train_parameters['image_enhance_strategy']['need_flip'] == True:
                        mirror = int(np.random.uniform(0, 2))
                        if mirror == 1:
                            img = img.transpose(Image.FLIP_LEFT_RIGHT)
                    # HWC--->CHW && normalized
                    img = np.array(img).astype('float32')
                    img -= train_parameters['mean_rgb']
                    img = img.transpose((2, 0, 1))  # HWC to CHW
                    img *= 0.007843                 # 像素值归一化
                    yield img, int(label)
                except Exception as e:
                    pass                            # 以防某些图片读取处理出错，加异常处理
            elif mode == 'test':
                img_path = os.path.join(data_dir, line)
                img = Image.open(img_path)
                if img.mode != 'RGB':
                    img = img.convert('RGB')
                img = resize_img(img, train_parameters['input_size'])
                # HWC--->CHW && normalized
                img = np.array(img).astype('float32')
                img -= train_parameters['mean_rgb']
                img = img.transpose((2, 0, 1))  # HWC to CHW
                img *= 0.007843  # 像素值归一化
                yield img

    return reader<jupyter_output><empty_output><jupyter_text>5. 不同类型优化器定义定义不同的优化器方便模型训练调用* Monmentum* RMS* SGD* ADAM<jupyter_code>def optimizer_momentum_setting():
    """
    阶梯型的学习率适合比较大规模的训练数据
    """
    learning_strategy = train_parameters['momentum_strategy']
    batch_size = train_parameters["train_batch_size"]
    iters = train_parameters["image_count"] // batch_size
    lr = learning_strategy['learning_rate']

    boundaries = [i * iters for i in learning_strategy["lr_epochs"]]
    values = [i * lr for i in learning_strategy["lr_decay"]]
    learning_rate = fluid.layers.piecewise_decay(boundaries, values)
    optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
    return optimizer


def optimizer_rms_setting():
    """
    阶梯型的学习率适合比较大规模的训练数据
    """
    batch_size = train_parameters["train_batch_size"]
    iters = train_parameters["image_count"] // batch_size
    learning_strategy = train_parameters['rsm_strategy']
    lr = learning_strategy['learning_rate']

    boundaries = [i * iters for i in learning_strategy["lr_epochs"]]
    values = [i * lr for i in learning_strategy["lr_decay"]]

    optimizer = fluid.optimizer.RMSProp(
        learning_rate=fluid.layers.piecewise_decay(boundaries, values))

    return optimizer


def optimizer_sgd_setting():
    """
    loss下降相对较慢，但是最终效果不错，阶梯型的学习率适合比较大规模的训练数据
    """
    learning_strategy = train_parameters['sgd_strategy']
    batch_size = train_parameters["train_batch_size"]
    iters = train_parameters["image_count"] // batch_size
    lr = learning_strategy['learning_rate']

    boundaries = [i * iters for i in learning_strategy["lr_epochs"]]
    values = [i * lr for i in learning_strategy["lr_decay"]]
    learning_rate = fluid.layers.piecewise_decay(boundaries, values)
    optimizer = fluid.optimizer.SGD(learning_rate=learning_rate)
    return optimizer


def optimizer_adam_setting():
    """
    能够比较快速的降低 loss，但是相对后期乏力
    """
    learning_strategy = train_parameters['adam_strategy']
    learning_rate = learning_strategy['learning_rate']
    optimizer = fluid.optimizer.Adam(learning_rate=learning_rate)
    return optimizer<jupyter_output><empty_output><jupyter_text>6. 模型数据加载及训练<jupyter_code>from visualdl import LogWriter

def load_params(exe, program):
    if train_parameters['continue_train'] and os.path.exists(train_parameters['save_persistable_dir']):
        logger.info('load params from retrain model')
        fluid.io.load_persistables(executor=exe,
                                   dirname=train_parameters['save_persistable_dir'],
                                   main_program=program)
    elif train_parameters['pretrained'] and os.path.exists(train_parameters['pretrained_dir']):
        logger.info('load params from pretrained model')
        def if_exist(var):
            return os.path.exists(os.path.join(train_parameters['pretrained_dir'], var.name))

        fluid.io.load_vars(exe, train_parameters['pretrained_dir'], main_program=program,
                           predicate=if_exist)


def train():
    with LogWriter(logdir="./log/scalar_test/train") as writer:
        pass
    train_prog = fluid.Program()
    train_startup = fluid.Program()
    logger.info("create prog success")
    logger.info("train config: %s", str(train_parameters))
    logger.info("build input custom reader and data feeder")
    file_list = os.path.join(train_parameters['data_dir'], "train.txt")
    mode = train_parameters['mode']
    batch_reader = paddle.batch(custom_image_reader(file_list, train_parameters['data_dir'], mode),
                                batch_size=train_parameters['train_batch_size'],
                                drop_last=True)
    place = fluid.CUDAPlace(0) if train_parameters['use_gpu'] else fluid.CPUPlace()
    # 定义输入数据的占位符
    img = fluid.layers.data(name='img', shape=train_parameters['input_size'], dtype='float32')
    label = fluid.layers.data(name='label', shape=[1], dtype='int64')
    feeder = fluid.DataFeeder(feed_list=[img, label], place=place)

    # 选取不同的网络
    logger.info("build newwork")
    model = ResNet()
    out = model.net(input=img, class_dim=train_parameters['class_dim'])
    cost = fluid.layers.cross_entropy(out, label)
    avg_cost = fluid.layers.mean(x=cost)
    acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1)
    # 选取不同的优化器
    # optimizer = optimizer_rms_setting()
    # optimizer = optimizer_momentum_setting()
    optimizer = optimizer_sgd_setting()
    # optimizer = optimizer_adam_setting()
    optimizer.minimize(avg_cost)
    exe = fluid.Executor(place)

    main_program = fluid.default_main_program()
    exe.run(fluid.default_startup_program())
    train_fetch_list = [avg_cost.name, acc_top1.name, out.name]
    
    load_params(exe, main_program)

    # 训练循环主体
    stop_strategy = train_parameters['early_stop']
    successive_limit = stop_strategy['successive_limit']
    sample_freq = stop_strategy['sample_frequency']
    good_acc1 = stop_strategy['good_acc1']
    successive_count = 0
    stop_train = False
    total_batch_count = 0
    for pass_id in range(train_parameters["num_epochs"]):
        logger.info("current pass: %d, start read image", pass_id)
        batch_id = 0
        for step_id, data in enumerate(batch_reader()):
            t1 = time.time()
            loss, acc1, pred_ot = exe.run(main_program,
                                          feed=feeder.feed(data),
                                          fetch_list=train_fetch_list)
            t2 = time.time()
            batch_id += 1
            total_batch_count += 1
            period = t2 - t1
            loss = np.mean(np.array(loss))
            acc1 = np.mean(np.array(acc1))
            writer.add_scalar(tag="acc", step=total_batch_count, value=acc1)
            writer.add_scalar(tag="loss", step=total_batch_count, value=loss)
            if batch_id % 10 == 0:
                logger.info("Pass {0}, trainbatch {1}, loss {2}, acc1 {3}, time {4}".format(pass_id, batch_id, loss, acc1,
                                                                                            "%2.2f sec" % period))
            # 简单的提前停止策略，认为连续达到某个准确率就可以停止了
            if acc1 >= good_acc1:
                successive_count += 1
                logger.info("current acc1 {0} meets good {1}, successive count {2}".format(acc1, good_acc1, successive_count))
                fluid.io.save_inference_model(dirname=train_parameters['save_freeze_dir'],
                                              feeded_var_names=['img'],
                                              target_vars=[out],
                                              main_program=main_program,
                                              executor=exe)
                if successive_count >= successive_limit:
                    logger.info("end training")
                    stop_train = True
                    break
            else:
                successive_count = 0

            # 通用的保存策略，减小意外停止的损失
            if total_batch_count % sample_freq == 0:
                logger.info("temp save {0} batch train result, current acc1 {1}".format(total_batch_count, acc1))
                fluid.io.save_persistables(dirname=train_parameters['save_persistable_dir'],
                                           main_program=main_program,
                                           executor=exe)
        if stop_train:
            break
    logger.info("training till last epcho, end training")
    fluid.io.save_persistables(dirname=train_parameters['save_persistable_dir'],
                                           main_program=main_program,
                                           executor=exe)
    fluid.io.save_inference_model(dirname=train_parameters['save_freeze_dir'],
                                              feeded_var_names=['img'],
                                              target_vars=[out],
                                              main_program=main_program,
                                              executor=exe)


if __name__ == '__main__':
    init_log_config()
    init_train_parameters()
    train()<jupyter_output>2022-02-27 22:33:13,474-INFO: create prog success
2022-02-27 22:33:13,474 - 855965282.py[line:23] - INFO: create prog success
2022-02-27 22:33:13,476-INFO: train config: {'input_size': [3, 224, 224], 'class_dim': 58, 'image_count': 4850, 'label_dict': {'0': 0, '1': 1, '10': 10, '11': 11, '12': 12, '13': 13, '14': 14, '15': 15, '16': 16, '17': 17, '18': 18, '19': 19, '2': 2, '20': 20, '21': 21, '22': 22, '23': 23, '24': 24, '25': 25, '26': 26, '27': 27, '28': 28, '29': 29, '3': 3, '30': 30, '31': 31, '32': 32, '33': 33, '34': 34, '35': 35, '36': 36, '37': 37, '38': 38, '39': 39, '4': 4, '40': 40, '41': 41, '42': 42, '43': 43, '44': 44, '45': 45, '46': 46, '47': 47, '48': 48, '49': 49, '5': 5, '50': 50, '51': 51, '52': 52, '53': 53, '54': 54, '55': 55, '56': 56, '57': 57, '6': 6, '7': 7, '8': 8, '9': 9}, 'data_dir': 'data/data107275/images/', 'train_file_list': 'train.txt', 'label_file': 'label_list.txt', 'save_freeze_dir': './freeze-model', 'save_persistable_dir': './persistable-params'[...]<jupyter_text>* 训练过程中，训练集精确度变化图![acc.png](attachment:ecc0943a-fec0-45cc-8437-8ff0ab873423.png)* 训练过程中，loss值变化图![loss.png](attachment:22dce50a-1d38-4881-93cf-e2e5b1a09ef4.png)训练结束后，模型对训练集数据识别最高精确度为96.09%。  五、模型评估该部分主要是对训练好的模型进行评估，可以是用验证集进行评估，或者是直接预测结果。评估结果和预测结果尽量展示出来，增加吸引力。<jupyter_code>from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import numpy as np
import random
import time
import codecs
import sys
import functools
import math
import pandas as pd
import paddle
import paddle.fluid as fluid
from paddle.fluid import core
from paddle.fluid.param_attr import ParamAttr
from PIL import Image, ImageEnhance
import matplotlib.pyplot as plt

target_size = [3, 224, 224]
mean_rgb = mean
data_dir = "data/data107275/images/"
eval_file = "eval.txt"
use_gpu = True
place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
exe = fluid.Executor(place)
save_freeze_dir = "./freeze-model"
[inference_program, feed_target_names, fetch_targets] = fluid.io.load_inference_model(dirname=save_freeze_dir, executor=exe)
# print(fetch_targets)


def crop_image(img, target_size):
    width, height = img.size
    w_start = (width - target_size[2]) / 2
    h_start = (height - target_size[1]) / 2
    w_end = w_start + target_size[2]
    h_end = h_start + target_size[1]
    img = img.crop((w_start, h_start, w_end, h_end))
    return img


def resize_img(img, target_size):
    ret = img.resize((target_size[1], target_size[2]), Image.BILINEAR)
    return ret


def read_image(img_path):
    img = Image.open(img_path)
    if img.mode != 'RGB':
        img = img.convert('RGB')
    img = crop_image(img, target_size)
    img = np.array(img).astype('float32')
    img -= mean_rgb
    img = img.transpose((2, 0, 1))  # HWC to CHW
    img *= 0.007843
    img = img[np.newaxis,:]
    return img


def infer(image_path):
    tensor_img = read_image(image_path)
    label = exe.run(inference_program, feed={feed_target_names[0]: tensor_img}, fetch_list=fetch_targets)
    return np.argmax(label)


def eval_all():
    eval_file_path = os.path.join(data_dir, eval_file)
    total_count = 0
    right_count = 0
    with codecs.open(eval_file_path, encoding='utf-8') as flist: 
        lines = [line.strip() for line in flist]
        t1 = time.time()
        for line in lines:
            total_count += 1
            parts = line.strip().split()
            result = infer(parts[0])
            if str(result) == parts[1]:
                right_count += 1
        period = time.time() - t1
        print("total eval count:{0} cost time:{1} predict accuracy:{2}".format(total_count, "%2.2f sec" % period, right_count / total_count))


def showOnePic():
    pic_file_path_list = os.path.join(data_dir, eval_file)
    df=pd.read_csv(pic_file_path_list, sep='\t', header=None)

    # 子图1
    random_idx = np.random.randint(0,df.shape[0])
    pic_file_path = df[0][random_idx]
    img = Image.open(pic_file_path)
    true_label = df[1][random_idx]
    pred_label = infer(pic_file_path)

    plt.subplot(221)
    plt.imshow(img)
    plt.title('True Label:{}\n Predict Label:{}'.format(true_label,pred_label))
    plt.axis('off')

    # 子图2
    random_idx = np.random.randint(0,df.shape[0])
    pic_file_path = df[0][random_idx]
    img = Image.open(pic_file_path)
    true_label = df[1][random_idx]
    pred_label = infer(pic_file_path)

    plt.subplot(222)
    plt.imshow(img)
    plt.title('True Label:{}\n Predict Label:{}'.format(true_label,pred_label))
    plt.axis('off')

    # 子图3
    random_idx = np.random.randint(0,df.shape[0])
    pic_file_path = df[0][random_idx]
    img = Image.open(pic_file_path)
    true_label = df[1][random_idx]
    pred_label = infer(pic_file_path)

    plt.subplot(223)
    plt.imshow(img)
    plt.title('True Label:{}\n Predict Label:{}'.format(true_label,pred_label))
    plt.axis('off')

    # 子图4
    random_idx = np.random.randint(0,df.shape[0])
    pic_file_path = df[0][random_idx]
    img = Image.open(pic_file_path)
    true_label = df[1][random_idx]
    pred_label = infer(pic_file_path)

    plt.subplot(224)
    plt.imshow(img)
    plt.title('True Label:{}\n Predict Label:{}'.format(true_label,pred_label))
    plt.axis('off')

    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    eval_all()
    showOnePic()<jupyter_output>total eval count:1148 cost time:35.76 sec predict accuracy:0.6445993031358885
{'fraction_non_alphanumeric': 0.1266499490203643, 'fraction_numerical': 0.033122984926561766, 'mean_word_length': 2.7454845701310764, 'pattern_counts': {'":': 49, '<': 44, '<?xml version=': 0, '>': 44, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 37, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '147290', 'license': 'MIT', 'n_tokens_mistral': 15247, 'n_tokens_neox': 15335, 'n_words': 2168, 'path': 'main.ipynb', 'repo_name': 'SyaoKyo/Paddle', 'size': '36298'}