代码复现python目标检测yolo3详解预测


Posted in Python onMay 06, 2022

学习前言

对yolo2解析完了之后当然要讲讲yolo3,yolo3与yolo2的差别主要在网络的特征提取部分,实际的解码部分其实差距不大

代码下载

本次教程主要基于github中的项目点击,该项目相比于yolo3-Keras的项目更容易看懂一些,不过它的许多代码与yolo3-Keras相同。

我保留了预测部分的代码,在实际可以通过执行detect.py运行示例。

链接: https://pan.baidu.com/s/1_xLeytnjBBSL2h2Kj4-YEw

取码:i3hi

实现思路

1、yolo3的预测思路(网络构建思路)

代码复现python目标检测yolo3详解预测

YOLOv3相比于之前的yolo1和yolo2,改进较大,主要改进方向有:

1、使用了残差网络Residual,残差卷积就是进行一次3X3、步长为2的卷积,然后保存该卷积layer,再进行一次1X1的卷积和一次3X3的卷积,并把这个结果加上layer作为最后的结果, 残差网络的特点是容易优化,并且能够通过增加相当的深度来提高准确率。其内部的残差块使用了跳跃连接,缓解了在深度神经网络中增加深度带来的梯度消失问题。

2、提取多特征层进行目标检测,一共提取三个特征层,特征层的shape分别为(13,13,75),(26,26,75),(52,52,75),最后一个维度为75是因为该图是基于voc数据集的,它的类为20种,yolo3只有针对每一个特征层存在3个先验框,所以最后维度为3x25;

如果使用的是coco训练集,类则为80种,最后的维度应该为255 = 3x85,三个特征层的shape为(13,13,255),(26,26,255),(52,52,255)

3、其采用反卷积UmSampling2d设计,逆卷积相对于卷积在神经网络结构的正向和反向传播中做相反的运算,其可以更多更好的提取出特征。

其实际情况就是,输入N张416x416的图片,在经过多层的运算后,会输出三个shape分别为(N,13,13,255),(N,26,26,255),(N,52,52,255)的数据,对应每个图分为13x13、26x26、52x52的网格上3个先验框的位置。

实现代码如下,其在实际调用时,会调用其中的yolo_inference函数,此时获得三个特征层的内容。

def _darknet53(self, inputs, conv_index, training = True, norm_decay = 0.99, norm_epsilon = 1e-3):
    """
    Introduction
    ------------
        构建yolo3使用的darknet53网络结构
    Parameters
    ----------
        inputs: 模型输入变量
        conv_index: 卷积层数序号,方便根据名字加载预训练权重
        weights_dict: 预训练权重
        training: 是否为训练
        norm_decay: 在预测时计算moving average时的衰减率
        norm_epsilon: 方差加上极小的数,防止除以0的情况
    Returns
    -------
        conv: 经过52层卷积计算之后的结果, 输入图片为416x416x3,则此时输出的结果shape为13x13x1024
        route1: 返回第26层卷积计算结果52x52x256, 供后续使用
        route2: 返回第43层卷积计算结果26x26x512, 供后续使用
        conv_index: 卷积层计数,方便在加载预训练模型时使用
    """
    with tf.variable_scope('darknet53'):
        # 416,416,3 -> 416,416,32
        conv = self._conv2d_layer(inputs, filters_num = 32, kernel_size = 3, strides = 1, name = "conv2d_" + str(conv_index))
        conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        conv_index += 1
        # 416,416,32 -> 208,208,64
        conv, conv_index = self._Residual_block(conv, conv_index = conv_index, filters_num = 64, blocks_num = 1, training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        # 208,208,64 -> 104,104,128
        conv, conv_index = self._Residual_block(conv, conv_index = conv_index, filters_num = 128, blocks_num = 2, training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        # 104,104,128 -> 52,52,256
        conv, conv_index = self._Residual_block(conv, conv_index = conv_index, filters_num = 256, blocks_num = 8, training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        # route1 = 52,52,256
        route1 = conv
        # 52,52,256 -> 26,26,512
        conv, conv_index = self._Residual_block(conv, conv_index = conv_index, filters_num = 512, blocks_num = 8, training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        # route2 = 26,26,512
        route2 = conv
        # 26,26,512 -> 13,13,1024
        conv, conv_index = self._Residual_block(conv, conv_index = conv_index,  filters_num = 1024, blocks_num = 4, training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
        # route3 = 13,13,1024
    return  route1, route2, conv, conv_index
# 输出两个网络结果
# 第一个是进行5次卷积后,用于下一次逆卷积的,卷积过程是1X1,3X3,1X1,3X3,1X1
# 第二个是进行5+2次卷积,作为一个特征层的,卷积过程是1X1,3X3,1X1,3X3,1X1,3X3,1X1
def _yolo_block(self, inputs, filters_num, out_filters, conv_index, training = True, norm_decay = 0.99, norm_epsilon = 1e-3):
    """
    Introduction
    ------------
        yolo3在Darknet53提取的特征层基础上,又加了针对3种不同比例的feature map的block,这样来提高对小物体的检测率
    Parameters
    ----------
        inputs: 输入特征
        filters_num: 卷积核数量
        out_filters: 最后输出层的卷积核数量
        conv_index: 卷积层数序号,方便根据名字加载预训练权重
        training: 是否为训练
        norm_decay: 在预测时计算moving average时的衰减率
        norm_epsilon: 方差加上极小的数,防止除以0的情况
    Returns
    -------
        route: 返回最后一层卷积的前一层结果
        conv: 返回最后一层卷积的结果
        conv_index: conv层计数
    """
    conv = self._conv2d_layer(inputs, filters_num = filters_num, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    conv = self._conv2d_layer(conv, filters_num = filters_num * 2, kernel_size = 3, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    conv = self._conv2d_layer(conv, filters_num = filters_num, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    conv = self._conv2d_layer(conv, filters_num = filters_num * 2, kernel_size = 3, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    conv = self._conv2d_layer(conv, filters_num = filters_num, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    route = conv
    conv = self._conv2d_layer(conv, filters_num = filters_num * 2, kernel_size = 3, strides = 1, name = "conv2d_" + str(conv_index))
    conv = self._batch_normalization_layer(conv, name = "batch_normalization_" + str(conv_index), training = training, norm_decay = norm_decay, norm_epsilon = norm_epsilon)
    conv_index += 1
    conv = self._conv2d_layer(conv, filters_num = out_filters, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index), use_bias = True)
    conv_index += 1
    return route, conv, conv_index
# 返回三个特征层的内容
def yolo_inference(self, inputs, num_anchors, num_classes, training = True):
    """
    Introduction
    ------------
        构建yolo模型结构
    Parameters
    ----------
        inputs: 模型的输入变量
        num_anchors: 每个grid cell负责检测的anchor数量
        num_classes: 类别数量
        training: 是否为训练模式
    """
    conv_index = 1
    # route1 = 52,52,256、route2 = 26,26,512、route3 = 13,13,1024
    conv2d_26, conv2d_43, conv, conv_index = self._darknet53(inputs, conv_index, training = training, norm_decay = self.norm_decay, norm_epsilon = self.norm_epsilon)
    with tf.variable_scope('yolo'):
        #--------------------------------------#
        #   获得第一个特征层
        #--------------------------------------#
        # conv2d_57 = 13,13,512,conv2d_59 = 13,13,255(3x(80+5))
        conv2d_57, conv2d_59, conv_index = self._yolo_block(conv, 512, num_anchors * (num_classes + 5), conv_index = conv_index, training = training, norm_decay = self.norm_decay, norm_epsilon = self.norm_epsilon)
        #--------------------------------------#
        #   获得第二个特征层
        #--------------------------------------#
        conv2d_60 = self._conv2d_layer(conv2d_57, filters_num = 256, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index))
        conv2d_60 = self._batch_normalization_layer(conv2d_60, name = "batch_normalization_" + str(conv_index),training = training, norm_decay = self.norm_decay, norm_epsilon = self.norm_epsilon)
        conv_index += 1
        # unSample_0 = 26,26,256
        unSample_0 = tf.image.resize_nearest_neighbor(conv2d_60, [2 * tf.shape(conv2d_60)[1], 2 * tf.shape(conv2d_60)[1]], name='upSample_0')
        # route0 = 26,26,768
        route0 = tf.concat([unSample_0, conv2d_43], axis = -1, name = 'route_0')
        # conv2d_65 = 52,52,256,conv2d_67 = 26,26,255
        conv2d_65, conv2d_67, conv_index = self._yolo_block(route0, 256, num_anchors * (num_classes + 5), conv_index = conv_index, training = training, norm_decay = self.norm_decay, norm_epsilon = self.norm_epsilon)
        #--------------------------------------#
        #   获得第三个特征层
        #--------------------------------------# 
        conv2d_68 = self._conv2d_layer(conv2d_65, filters_num = 128, kernel_size = 1, strides = 1, name = "conv2d_" + str(conv_index))
        conv2d_68 = self._batch_normalization_layer(conv2d_68, name = "batch_normalization_" + str(conv_index), training=training, norm_decay=self.norm_decay, norm_epsilon = self.norm_epsilon)
        conv_index += 1
        # unSample_1 = 52,52,128
        unSample_1 = tf.image.resize_nearest_neighbor(conv2d_68, [2 * tf.shape(conv2d_68)[1], 2 * tf.shape(conv2d_68)[1]], name='upSample_1')
        # route1= 52,52,384
        route1 = tf.concat([unSample_1, conv2d_26], axis = -1, name = 'route_1')
        # conv2d_75 = 52,52,255
        _, conv2d_75, _ = self._yolo_block(route1, 128, num_anchors * (num_classes + 5), conv_index = conv_index, training = training, norm_decay = self.norm_decay, norm_epsilon = self.norm_epsilon)
    return [conv2d_59, conv2d_67, conv2d_75]

2、利用先验框对网络的输出进行解码

yolo3的先验框生成与yolo2的类似,其实yolo3的解码与yolo2的解码过程一样,只是对于yolo3而言,其需要对三个特征层进行解码,三个特征层的shape分别为(N,13,13,255),(N,26,26,255),(N,52,52,255)的数据,对应每个图分为13x13、26x26、52x52的网格上3个先验框的位置。

此处需要用到一个循环。

1、将第一个特征层reshape成[-1, 13, 13, 3, 80 + 5],代表169个中心点每个中心点的3个先验框的情况。

2、将80+5的5中的xywh分离出来,0、1是xy相对中心点的偏移量;2、3是宽和高的情况;4是置信度。

3、建立13x13的网格,代表图片进行13x13处理后网格的中心点。

4、利用计算公式计算实际的bbox的位置 。

5、置信度乘上80+5中的80(这里的80指的是类别概率)得到得分。

6、将第二个特征层reshape成[-1, 26, 26, 3, 80 + 5],重复2到5步。将第三个特征层reshape成[-1, 52, 52, 3, 80 + 5],重复2到5步。

单个特征层的解码部分代码如下:

# 获得单个特征层框的位置和得分
def boxes_and_scores(self, feats, anchors, classes_num, input_shape, image_shape):
    """
    Introduction
    ------------
        将预测出的box坐标转换为对应原图的坐标,然后计算每个box的分数
    Parameters
    ----------
        feats: yolo输出的feature map
        anchors: anchor的位置
        class_num: 类别数目
        input_shape: 输入大小
        image_shape: 图片大小
    Returns
    -------
        boxes: 物体框的位置
        boxes_scores: 物体框的分数,为置信度和类别概率的乘积
    """
    # 获得特征
    box_xy, box_wh, box_confidence, box_class_probs = self._get_feats(feats, anchors, classes_num, input_shape)
    # 寻找在原图上的位置
    boxes = self.correct_boxes(box_xy, box_wh, input_shape, image_shape)
    boxes = tf.reshape(boxes, [-1, 4])
    # 获得置信度box_confidence * box_class_probs
    box_scores = box_confidence * box_class_probs
    box_scores = tf.reshape(box_scores, [-1, classes_num])
    return boxes, box_scores
# 单个特征层的解码过程
def _get_feats(self, feats, anchors, num_classes, input_shape):
    """
    Introduction
    ------------
        根据yolo最后一层的输出确定bounding box
    Parameters
    ----------
        feats: yolo模型最后一层输出
        anchors: anchors的位置
        num_classes: 类别数量
        input_shape: 输入大小
    Returns
    -------
        box_xy, box_wh, box_confidence, box_class_probs
    """
    num_anchors = len(anchors)
    anchors_tensor = tf.reshape(tf.constant(anchors, dtype=tf.float32), [1, 1, 1, num_anchors, 2])
    grid_size = tf.shape(feats)[1:3]
    predictions = tf.reshape(feats, [-1, grid_size[0], grid_size[1], num_anchors, num_classes + 5])
    # 这里构建13*13*1*2的矩阵,对应每个格子加上对应的坐标
    grid_y = tf.tile(tf.reshape(tf.range(grid_size[0]), [-1, 1, 1, 1]), [1, grid_size[1], 1, 1])
    grid_x = tf.tile(tf.reshape(tf.range(grid_size[1]), [1, -1, 1, 1]), [grid_size[0], 1, 1, 1])
    grid = tf.concat([grid_x, grid_y], axis = -1)
    grid = tf.cast(grid, tf.float32)
    # 将x,y坐标归一化,相对网格的位置
    box_xy = (tf.sigmoid(predictions[..., :2]) + grid) / tf.cast(grid_size[::-1], tf.float32)
    # 将w,h也归一化
    box_wh = tf.exp(predictions[..., 2:4]) * anchors_tensor / tf.cast(input_shape[::-1], tf.float32)
    box_confidence = tf.sigmoid(predictions[..., 4:5])
    box_class_probs = tf.sigmoid(predictions[..., 5:])
    return box_xy, box_wh, box_confidence, box_class_probs

该函数被其它函数调用,用于完成三个特征层的解码:

def eval(self, yolo_outputs, image_shape, max_boxes = 20):
    """
    Introduction
    ------------
        根据Yolo模型的输出进行非极大值抑制,获取最后的物体检测框和物体检测类别
    Parameters
    ----------
        yolo_outputs: yolo模型输出
        image_shape: 图片的大小
        max_boxes:  最大box数量
    Returns
    -------
        boxes_: 物体框的位置
        scores_: 物体类别的概率
        classes_: 物体类别
    """
    # 每一个特征层对应三个先验框
    anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
    boxes = []
    box_scores = []
    # inputshape是416x416
    # image_shape是实际图片的大小
    input_shape = tf.shape(yolo_outputs[0])[1 : 3] * 32
    # 对三个特征层的输出获取每个预测box坐标和box的分数,score = 置信度x类别概率
    #---------------------------------------#
    #   对三个特征层解码
    #   获得分数和框的位置
    #---------------------------------------#
    for i in range(len(yolo_outputs)):
        _boxes, _box_scores = self.boxes_and_scores(yolo_outputs[i], self.anchors[anchor_mask[i]], len(self.class_names), input_shape, image_shape)
        boxes.append(_boxes)
        box_scores.append(_box_scores)
    # 放在一行里面便于操作
    boxes = tf.concat(boxes, axis = 0)
    box_scores = tf.concat(box_scores, axis = 0)

3、进行得分排序与非极大抑制筛选

这一部分基本上是所有目标检测通用的部分。不过该项目的处理方式与其它项目不同。其对于每一个类进行判别。

1、取出每一类得分大于self.obj_threshold的框和得分。

2、利用框的位置和得分进行非极大抑制。

实现代码如下:

#---------------------------------------#
    #   1、取出每一类得分大于self.obj_threshold
    #   的框和得分
    #   2、对得分进行非极大抑制
    #---------------------------------------#
    # 对每一个类进行判断
    for c in range(len(self.class_names)):
        # 取出所有类为c的box
        class_boxes = tf.boolean_mask(boxes, mask[:, c])
        # 取出所有类为c的分数
        class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c])
        # 非极大抑制
        nms_index = tf.image.non_max_suppression(class_boxes, class_box_scores, max_boxes_tensor, iou_threshold = self.nms_threshold)
        # 获取非极大抑制的结果
        class_boxes = tf.gather(class_boxes, nms_index)
        class_box_scores = tf.gather(class_box_scores, nms_index)
        classes = tf.ones_like(class_box_scores, 'int32') * c
        boxes_.append(class_boxes)
        scores_.append(class_box_scores)
        classes_.append(classes)
    boxes_ = tf.concat(boxes_, axis = 0)
    scores_ = tf.concat(scores_, axis = 0)
    classes_ = tf.concat(classes_, axis = 0)

实际上该部分与第二部分在一个函数里,完成输出的解码和筛选,完成预测过程。

def eval(self, yolo_outputs, image_shape, max_boxes = 20):
    """
    Introduction
    ------------
        根据Yolo模型的输出进行非极大值抑制,获取最后的物体检测框和物体检测类别
    Parameters
    ----------
        yolo_outputs: yolo模型输出
        image_shape: 图片的大小
        max_boxes:  最大box数量
    Returns
    -------
        boxes_: 物体框的位置
        scores_: 物体类别的概率
        classes_: 物体类别
    """
    # 每一个特征层对应三个先验框
    anchor_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
    boxes = []
    box_scores = []
    # inputshape是416x416
    # image_shape是实际图片的大小
    input_shape = tf.shape(yolo_outputs[0])[1 : 3] * 32
    # 对三个特征层的输出获取每个预测box坐标和box的分数,score = 置信度x类别概率
    #---------------------------------------#
    #   对三个特征层解码
    #   获得分数和框的位置
    #---------------------------------------#
    for i in range(len(yolo_outputs)):
        _boxes, _box_scores = self.boxes_and_scores(yolo_outputs[i], self.anchors[anchor_mask[i]], len(self.class_names), input_shape, image_shape)
        boxes.append(_boxes)
        box_scores.append(_box_scores)
    # 放在一行里面便于操作
    boxes = tf.concat(boxes, axis = 0)
    box_scores = tf.concat(box_scores, axis = 0)
    mask = box_scores >= self.obj_threshold
    max_boxes_tensor = tf.constant(max_boxes, dtype = tf.int32)
    boxes_ = []
    scores_ = []
    classes_ = []
    #---------------------------------------#
    #   1、取出每一类得分大于self.obj_threshold
    #   的框和得分
    #   2、对得分进行非极大抑制
    #---------------------------------------#
    # 对每一个类进行判断
    for c in range(len(self.class_names)):
        # 取出所有类为c的box
        class_boxes = tf.boolean_mask(boxes, mask[:, c])
        # 取出所有类为c的分数
        class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c])
        # 非极大抑制
        nms_index = tf.image.non_max_suppression(class_boxes, class_box_scores, max_boxes_tensor, iou_threshold = self.nms_threshold)
        # 获取非极大抑制的结果
        class_boxes = tf.gather(class_boxes, nms_index)
        class_box_scores = tf.gather(class_box_scores, nms_index)
        classes = tf.ones_like(class_box_scores, 'int32') * c
        boxes_.append(class_boxes)
        scores_.append(class_box_scores)
        classes_.append(classes)
    boxes_ = tf.concat(boxes_, axis = 0)
    scores_ = tf.concat(scores_, axis = 0)
    classes_ = tf.concat(classes_, axis = 0)
    return boxes_, scores_, classes_

得到框的位置和种类后就可以画图了。

实现结果

代码复现python目标检测yolo3详解预测

以上就是python目标检测yolo3详解预测及代码复现的详细内容!


Tags in this post...

Python 相关文章推荐
利用Celery实现Django博客PV统计功能详解
May 08 Python
python 3.6 +pyMysql 操作mysql数据库(实例讲解)
Dec 20 Python
Python实现判断字符串中包含某个字符的判断函数示例
Jan 08 Python
使用 Python 实现文件递归遍历的三种方式
Jul 18 Python
使用python对excle和json互相转换的示例
Oct 23 Python
Python 从一个文件中调用另一个文件的类方法
Jan 10 Python
Python的对象传递与Copy函数使用详解
Dec 26 Python
Pycharm pyuic5实现将ui文件转为py文件,让UI界面成功显示
Apr 08 Python
Python 使用xlwt模块将多行多列数据循环写入excel文档的操作
Nov 10 Python
详解java调用python的几种用法(看这篇就够了)
Dec 10 Python
python 基于opencv实现图像增强
Dec 23 Python
Python使用Web框架Flask开发项目
Jun 01 Python
讲解Python实例练习逆序输出字符串
May 06 #Python
python turtle绘图
May 04 #Python
python blinker 信号库
May 04 #Python
python三子棋游戏
May 04 #Python
python神经网络 使用Keras构建RNN训练
May 04 #Python
python神经网络学习 使用Keras进行回归运算
May 04 #Python
python神经网络学习 使用Keras进行简单分类
May 04 #Python
You might like
PHP 计算代码执行耗时的代码修正网上普遍错误
2011/05/14 PHP
php设计模式 Singleton(单例模式)
2011/06/26 PHP
分享一个自己写的简单的javascript分页组件
2015/02/15 Javascript
JS使用ajax从xml文件动态获取数据显示的方法
2015/03/24 Javascript
jQuery使用post方法提交数据实例
2015/03/25 Javascript
JavaScript中对DOM节点的访问、创建、修改、删除
2015/11/16 Javascript
bootstrap中使用google prettify让代码高亮的方法
2016/10/21 Javascript
js获取浏览器的各种属性
2017/04/27 Javascript
基于jQuery实现图片推拉门动画效果的两种方法
2017/08/26 jQuery
jQuery选择器之表单元素选择器详解
2017/09/19 jQuery
JavaScript实现打印星型金字塔功能实例分析
2017/09/27 Javascript
小程序实现tab标签页
2020/11/16 Javascript
[02:06]DOTA2肉山黑名单魔法终结者 敌法师中文配音鉴赏
2013/06/17 DOTA
Python使用cx_Oracle调用Oracle存储过程的方法示例
2017/10/07 Python
Django框架多表查询实例分析
2018/07/04 Python
使用 Visual Studio Code(VSCode)搭建简单的Python+Django开发环境的方法步骤
2018/12/17 Python
python dict 相同key 合并value的实例
2019/01/21 Python
Django 中自定义 Admin 样式与功能的实现方法
2019/07/04 Python
python之生产者消费者模型实现详解
2019/07/27 Python
Python封装成可带参数的EXE安装包实例
2019/08/24 Python
python 逆向爬虫正确调用 JAR 加密逻辑
2021/01/12 Python
关于h5中的fetch方法解读(小结)
2017/11/15 HTML / CSS
Russell Stover巧克力官方网站:美国领先的精美巧克力制造商
2016/11/27 全球购物
意大利简约的休闲品牌:Aspesi
2018/02/08 全球购物
澳大利亚波西米亚风连衣裙在线商店:Fortunate One
2019/04/01 全球购物
自荐信格式的六要素
2013/09/21 职场文书
《走一步再走一步》教学反思
2014/02/15 职场文书
总经理工作职责范文
2014/03/14 职场文书
医院护士党的群众路线教育实践活动对照检查材料思想汇报
2014/10/04 职场文书
中小学生学籍证明
2014/10/25 职场文书
公司2014年度工作总结
2014/12/10 职场文书
集团财务总监岗位职责
2015/04/03 职场文书
2015年行政部工作总结
2015/04/28 职场文书
幼儿园新生开学寄语
2015/05/27 职场文书
2016年基层党组织公开承诺书
2016/03/25 职场文书
MySQL实例精讲单行函数以及字符数学日期流程控制
2021/10/15 MySQL