怎么理解layui里的data_深入理解YOLO v3实现细节 - 第2篇 backbone&network

c3c0b605e20f04e153a62628d9bd7f74.png

深入理解YOLO v3实现细节系列文章,是本人根据自己对YOLO v3原理的理解,结合开源项目tensorflow-yolov3,写的学习笔记。如有不正确的地方,请大佬们指出,谢谢!

目录

第1篇 数据预处理

第2篇 backbone&network

第3篇 构建v3的 Loss_layer

今天来讲讲YOLO v3的backbone——darknet53。YOLO每一代的提升很大一部分决定于backbone网络的提升,从v2的darknet-19到v3的darknet-53。本文主要讲解darknet-53的结构以及darknet-53的tf代码实现,下面切入正题。

1 backbone骨干

1.1Darknet-53的网络结构

6f8eb937d62b8e4908d382d84689a3d7.png

Darknet-53 的主体框架如图所示,它主要由ConvolutionalResidual结构所组成。可以看到Darknet-53没有池化层和全连接层。张量的尺寸变换是通过改变卷积核的步长来实现。需要特别注意的是,最后三层AvgpoolConnectedsoftmaxlayer 是用于在Imagenet数据集上作分类训练用的。当我们用 Darknet-53 层对图片提取特征时,是不会用到这三层的。

27244d24947da86d31e3d4bdf0b80d6b.png

代码结构里的downsample参数的意思是下采样,表示 feature map 输入该层 layer 后尺寸会变小。例如在第二层 layer 的输入尺寸是256X256,输出尺寸则变成了128X128,这就等于将图像边长缩小了一半(即面积缩小到原来的1/4)。 可以看到downsample出现了5次,特征图缩小到原输入尺寸的

,即1/32,所以输入图片的尺寸通常是32的倍数。详细请阅读backbone.py。

1.2 Convolutional 结构

def convolutional(input_data, filters_shape, trainable, name, downsample=False, activate=True, bn=True):

    with tf.variable_scope(name):
        if downsample:
            pad_h, pad_w = (filters_shape[0] - 2) // 2 + 1, (filters_shape[1] - 2) // 2 + 1
            paddings = tf.constant([[0, 0], [pad_h, pad_h], [pad_w, pad_w], [0, 0]])
            input_data = tf.pad(input_data, paddings, 'CONSTANT')
            strides = (1, 2, 2, 1)
            padding = 'VALID'
        else:
            strides = (1, 1, 1, 1)
            padding = "SAME"

        weight = tf.get_variable(name='weight', dtype=tf.float32, trainable=True,
                                 shape=filters_shape, initializer=tf.random_normal_initializer(stddev=0.01))
        conv = tf.nn.conv2d(input=input_data, filter=weight, strides=strides, padding=padding)

        if bn:
            conv = tf.layers.batch_normalization(conv, beta_initializer=tf.zeros_initializer(),
                                                 gamma_initializer=tf.ones_initializer(),
                                                 moving_mean_initializer=tf.zeros_initializer(),
                                                 moving_variance_initializer=tf.ones_initializer(), training=trainable)
        else:
            bias = tf.get_variable(name='bias', shape=filters_shape[-1], trainable=True,
                                   dtype=tf.float32, initializer=tf.constant_initializer(0.0))
            conv = tf.nn.bias_add(conv, bias)

        if activate == True: conv = tf.nn.leaky_relu(conv, alpha=0.1)

    return conv

对于if downsample的情况,需要对图像进行padding。

input_data形状为[batch, height, width, channels],所以padding相对应也是4维。这里只对height,和width进行padding,batch和channels维度对应padding的大小设为0就ok了。对于else的情况,padding设置为"SAME",步长设为1。BN层这里不再多说了。默认使用的激活函数是leaky_relu。简单说一下leaky_relu激活函数。ReLU是将所有的负值都设为零,相反地,Leaky ReLU是给所有负值赋予一个非零斜率。以数学的方式我们可以表示为:

6c2db48633f760cdeab44334d20ff3a4.png
Leaky ReLU激活函数

其中,ai是(1,+∞)区间内的固定参数。

1.3 Residual 残差模块

借鉴了ResNet的残差结构,使得网络结构更深,从v2的darknet-19上升到v3的darknet-53。

残差模块最显著的特点是使用了 short cut 机制(有点类似于电路中的短路机制)来缓解在神经网络中增加深度带来的梯度消失问题,从而使得神经网络变得更容易优化。它通过恒等映射(identity mapping)的方法使得输入和输出之间建立了一条直接的关联通道,从而使得网络集中学习输入和输出之间的残差。

b61f70d72962656ac252cf4b87341d1a.png
残差网络结构
def residual_block(input_data, input_channel, filter_num1, filter_num2, trainable, name):

    short_cut = input_data

    with tf.variable_scope(name):
        input_data = convolutional(input_data, filters_shape=(1, 1, input_channel, filter_num1),
                                   trainable=trainable, name='conv1')
        input_data = convolutional(input_data, filters_shape=(3, 3, filter_num1,   filter_num2),
                                   trainable=trainable, name='conv2')

        residual_output = input_data + short_cut

    return residual_output

1个残差结构包含2个convolutional层,第一层的卷积核是1×1,第二层的卷积核是3×3。

1.4 upsample上采样模块

def upsample(input_data, name, method="deconv"):
    assert method in ["resize", "deconv"]  
    if method == "resize":
        with tf.variable_scope(name):
            input_shape = tf.shape(input_data)
            output = tf.image.resize_nearest_neighbor(input_data, (input_shape[1] * 2, input_shape[2] * 2))

    if method == "deconv":
        # replace resize_nearest_neighbor with conv2d_transpose To support TensorRT optimization
        numm_filter = input_data.shape.as_list()[-1]
        output = tf.layers.conv2d_transpose(input_data, numm_filter, kernel_size=2, padding='same',
                                            strides=(2,2), kernel_initializer=tf.random_normal_initializer())

    return output

定义的上采样函数input_data的形状为[batch, height, width, channels]。然后给出了2种方式,第1种方式是resize,使用tf.image.resize_nearest_neighbor函数,输出的大小尺寸为height2, width*2。第2钟方式是deconv,顾名思义是反卷积。先获取numm filter滤波器的数量,与input_data的channels相同,使用tf.layers.conv2d_transpose函数进行反卷积,padding方式为SAME,卷积核大小尺寸为2,步长设置为2。假设输入的图片尺寸为416×416,经过5次下采样,feature map的大小为13×13×1024。那么经过上采样,输出大小为26×26×1024。

1.5 route模块

def route(name, previous_output, current_output):

    with tf.variable_scope(name):
        output = tf.concat([current_output, previous_output], axis=-1) #axis=-1表示倒数第1个维度

    return output

这个模块用于YOLO v3网络中的合并部分。

接下来,我们来看看YOLO v3是怎么样预测的

2. network网络

2.1 YOLOv3 的网格思想

YOLOv3 对输入图片进行了粗、中和细网格划分,以便分别实现对大、中和小物体的预测。其实在下面这幅图里面,每一个网格对应的就是一块 ROI 区域。如果某个物体的中心刚好落在这个网格中,那么这个网格就负责预测这个物体。

If the center of an object falls into a grid cell, that grid cell is responsible for detecting that object.

假如输入图片的尺寸为 416X416, 那么得到粗、中和细网格尺寸分别为 13X1326X2652X52。这样一算,那就是在长宽尺寸上分别缩放了32168倍,其实这些倍数正好也是这些 ROI 的尺寸大小。

323ba4ecfca1687c520f70e16af88f3a.png
粗网格

02c8f2181c340bbf295b3da45eaed6b2.png
中网格

2106b555d9cdbfca7704e434301f5c4a.png
细网格

2.2 准备图片

在将图片输入模型之前,需要将图片尺寸 resize 成固定的大小,如 416X416 或 608X608 。如果直接对图片进行 resize 处理,那么会使得图片扭曲变形从而降低模型的预测精度。

def image_preporcess(image, target_size, gt_boxes=None):

    ih, iw    = target_size # resize 尺寸
    h,  w, _  = image.shape # 原始图片尺寸

    scale = min(iw/w, ih/h)
    nw, nh  = int(scale * w), int(scale * h) # 计算缩放后图片尺寸
    image_resized = cv2.resize(image, (nw, nh))
    # 制作一张画布,画布的尺寸就是我们想要的尺寸
    image_paded = np.full(shape=[ih, iw, 3], fill_value=128.0)
    dw, dh = (iw - nw) // 2, (ih-nh) // 2
    # 将缩放后的图片放在画布中央
    image_paded[dh:nh+dh, dw:nw+dw, :] = image_resized
    image_paded = image_paded / 255.

    if gt_boxes is None:
        return image_paded

    else:   # 训练网络时需要对 groudtruth box 进行矫正
        gt_boxes[:, [0, 2]] = gt_boxes[:, [0, 2]] * scale + dw
        gt_boxes[:, [1, 3]] = gt_boxes[:, [1, 3]] * scale + dh
        return image_paded, gt_boxes

来看看效果,直接上图!

34e8dfb41861d77fa8f6106d5416131b.png
original_image (768X576)

41b66753dc11d93e838c8d08cef660de.png
letterbox_image (416X416)

2.3 网络输出

下面这幅图就是 YOLOv3 网络的整体结构,在图中我们可以看到:尺寸为 416X416 的输入图片进入 Darknet-53 网络后得到了 3 个分支,这些分支在经过一系列的卷积、上采样以及合并等操作后最终得到了三个尺寸不一的 feature map,形状分别为 [13, 13, 255]、[26, 26, 255] 和 [52, 52, 255]。

4daafa132d7f0a374edb99c6122f4d80.png

为了让yolo_v3结构图更好理解,对上图做一些补充解释:

DBL: DBL = conv + BN + Leaky relu. 上图左下角所示,就是文章前面介绍过的Convolutional 结构,YOLO v3的基本组件。对于v3来说,BN和leaky relu已经是和卷积层不可分离的部分了(最后一层卷积除外),共同构成了最小组件。

res unit:上图下方所示,文章前面介绍过的Residual 残差模块,也是YOLO v3的基本组件。

resn:n代表数字,有res1,res2, res4, res8,表示这个res_block里含有多少个res_unit。例如res8,表示这个res_block里有8个res_unit。这是yolo_v3的大组件,对于res_block的解释,可以在上图的右下角直观看到,基本组件也是DBL。

concat:张量拼接。将darknet中间层和后面的某一层的上采样进行拼接。拼接的操作和残差层add的操作是不一样的,拼接会扩充张量的维度,而add只是直接相加不会导致张量维度的改变。

讲了这么多,还是不如看代码来得亲切。

    def __build_nework(self, input_data):
        # 输入层进入 Darknet-53 网络后,得到了三个分支
        route_1, route_2, input_data = backbone.darknet53(input_data, self.trainable)

        # 见上图中的橘黄色模块(DBL),一共需要进行5次卷积操作
        input_data = common.convolutional(input_data, (1, 1, 1024,  512), self.trainable, 'conv52')
        input_data = common.convolutional(input_data, (3, 3,  512, 1024), self.trainable, 'conv53')
        input_data = common.convolutional(input_data, (1, 1, 1024,  512), self.trainable, 'conv54')
        input_data = common.convolutional(input_data, (3, 3,  512, 1024), self.trainable, 'conv55')
        input_data = common.convolutional(input_data, (1, 1, 1024,  512), self.trainable, 'conv56')

        # 橘黄色模块(DBL)
        conv_lobj_branch = common.convolutional(input_data, (3, 3, 512, 1024), self.trainable, name='conv_lobj_branch')
        
        # 蓝色模块(conv)      
        conv_lbbox = common.convolutional(conv_lobj_branch, (1, 1, 1024, 3*(self.num_class + 5)),
                                          trainable=self.trainable, name='conv_lbbox', activate=False, bn=False)

输出y1,shape = [None, 13, 13, 255]。conv_lbbox 用于预测大尺寸物体(参考2.1网格思想中的细网格图片)。

        input_data = common.convolutional(input_data, (1, 1,  512,  256), self.trainable, 'conv57')
        # 这里的 upsample 使用的是最近邻插值方法,这样的好处在于上采样过程不需要学习,从而减少了网络参数
        input_data = common.upsample(input_data, name='upsample0', method=self.upsample_method)

        with tf.variable_scope('route_1'):
            input_data = tf.concat([input_data, route_2], axis=-1)

        input_data = common.convolutional(input_data, (1, 1, 768, 256), self.trainable, 'conv58')
        input_data = common.convolutional(input_data, (3, 3, 256, 512), self.trainable, 'conv59')
        input_data = common.convolutional(input_data, (1, 1, 512, 256), self.trainable, 'conv60')
        input_data = common.convolutional(input_data, (3, 3, 256, 512), self.trainable, 'conv61')
        input_data = common.convolutional(input_data, (1, 1, 512, 256), self.trainable, 'conv62')

        # 橘黄色模块(DBL)
        conv_mobj_branch = common.convolutional(input_data, (3, 3, 256, 512),  self.trainable, name='conv_mobj_branch' )
        # 蓝色模块(conv)
        conv_mbbox = common.convolutional(conv_mobj_branch, (1, 1, 512, 3*(self.num_class + 5)),
                                          trainable=self.trainable, name='conv_mbbox', activate=False, bn=False)

输出y2,shape = [None, 26, 26, 255]。conv_mbbox 用于预测中等尺寸物体(参考2.1网格思想中的中网格图片)。

        input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv63')
        input_data = common.upsample(input_data, name='upsample1', method=self.upsample_method)

        with tf.variable_scope('route_2'):
            input_data = tf.concat([input_data, route_1], axis=-1)

        input_data = common.convolutional(input_data, (1, 1, 384, 128), self.trainable, 'conv64')
        input_data = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, 'conv65')
        input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv66')
        input_data = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, 'conv67')
        input_data = common.convolutional(input_data, (1, 1, 256, 128), self.trainable, 'conv68')

        # 橘黄色模块(DBL)
        conv_sobj_branch = common.convolutional(input_data, (3, 3, 128, 256), self.trainable, name='conv_sobj_branch')
        # 蓝色模块(conv)
        conv_sbbox = common.convolutional(conv_sobj_branch, (1, 1, 256, 3*(self.num_class + 5)),
                                          trainable=self.trainable, name='conv_sbbox', activate=False, bn=False)

输出y3,shape = [None, 52, 52, 255]。conv_sbbox 用于预测中等尺寸物体(参考2.1网格思想中的粗网格图片)。

return [conv_sbbox, conv_mbbox, conv_lbbox]

e49b4d39c9ac1af517c47f297d24bd5c.png

yolo v3输出了3个不同尺度的feature map,如上图所示的y1, y2, y3。这也是v3论文中提到的为数不多的改进点:predictions across scales。这个借鉴了FPN(feature pyramid networks),采用多尺度来对不同size的目标进行检测,越精细的grid cell就可以检测出越精细的物体。y1,y2和y3的深度都是255,边长的规律是13:26:52。

对于COCO类别而言,有80个种类,所以每个box应该对每个种类都输出一个概率。yolo v3设定的是每个网格单元预测3个box,所以每个box需要有(x, y, w, h, confidence)五个基本参数,还要有80个类别的概率。所以3*(5 + 80) = 255。这个255就是这么来的。(还记得yolo v1的输出张量吗? 7x7x30,只能识别20类物体,而且每个cell只能预测2个box)

YOLO v3用上采样的方法来实现这种多尺度的feature map,concat连接的两个张量是具有一样尺度的(两处拼接分别是26x26尺度拼接和52x52尺度拼接,通过(2, 2)上采样来保证concat拼接的张量尺度相同)。

2.4 边界框的预测

如果物体的中心落在了这个网格里,那么这个网格就要负责去预测它。在下面这幅图里:黑色虚线框代表先验框(anchor),蓝色框表示的是预测框。

98447b2e44cfe84a930139149b8d370c.png
  • b_h 和 b_w 分别表示预测框的长宽,P_h 和 P_w 分别表示先验框的长和宽。
  • t_x 和 t_y 表示的是物体中心距离网格左上角位置的偏移量,C_x 和 C_y 则代表网格左上角的坐标。

接下来,按照上面的公式,定义decode函数。

    def decode(self, conv_output, anchors, stride):
        """
        return tensor of shape [batch_size, output_size, output_size, anchor_per_scale, 5 + num_classes]
               contains (x, y, w, h, score, probability)
        """
        # stride 分别对应三种网格尺度
        # conv_output的形状为[batch_size, output_size, output_size, anchor_per_scale * (5 + num_classes)]
        conv_shape       = tf.shape(conv_output)
        batch_size       = conv_shape[0]
        output_size      = conv_shape[1]
        anchor_per_scale = len(anchors) # 3

        conv_output = tf.reshape(conv_output, (batch_size, output_size, output_size, anchor_per_scale, 5 + self.num_class))

        conv_raw_dxdy = conv_output[:, :, :, :, 0:2] # 中心位置的偏移量
        conv_raw_dwdh = conv_output[:, :, :, :, 2:4] # 预测框长宽的偏移量
        conv_raw_conf = conv_output[:, :, :, :, 4:5]
        conv_raw_prob = conv_output[:, :, :, :, 5: ]

        # 好了,接下来需要画网格了。其中,output_size 等于 13、26 或者 52        
        y = tf.tile(tf.range(output_size, dtype=tf.int32)[:, tf.newaxis], [1, output_size])        
        x = tf.tile(tf.range(output_size, dtype=tf.int32)[tf.newaxis, :], [output_size, 1])        
        xy_grid = tf.concat([x[:, :, tf.newaxis], y[:, :, tf.newaxis]], axis=-1)        
        xy_grid = tf.tile(xy_grid[tf.newaxis, :, :, tf.newaxis, :], [batch_size, 1, 1, anchor_per_scale, 1])        
        xy_grid = tf.cast(xy_grid, tf.float32) # 计算网格左上角的位置,即C_x和C_y

        # 根据上图公式计算预测框的中心位置
        pred_xy = (tf.sigmoid(conv_raw_dxdy) + xy_grid) * stride 
        # 根据上图公式计算预测框的长和宽大小
        pred_wh = (tf.exp(conv_raw_dwdh) * anchors) * stride
        # 合并边界框的位置和长宽信息
        pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1)

        pred_conf = tf.sigmoid(conv_raw_conf) # 计算预测框里object的置信度
        pred_prob = tf.sigmoid(conv_raw_prob) # 计算预测框里object的类别概率

        return tf.concat([pred_xywh, pred_conf, pred_prob], axis=-1)

输出预测框bboxes[batch_size, output_size, output_size, anchor_per_scale, 5 (x, y, w, h, score)+num_classes(probability)]

2.5 NMS处理

对预测框bboxes进行非极大值抑制(NMS)处理,选取最好的best_bboxes作为最终的预测框输出。

NMS算法流程:

98f56ee8ada1559f679a16a2472a6686.png

非极大值抑制(Non-Maximum Suppression,NMS),顾名思义就是抑制不是极大值的元素,说白了就是去除掉那些重叠率较高并且 score 评分较低的边界框。 NMS 的算法非常简单,迭代流程如下:

  • 流程1: 判断边界框的数目是否大于0,如果不是则结束迭代;
  • 流程2: 按照 socre 排序选出评分最大的边界框 A 并取出;
  • 流程3: 计算这个边界框 A 与剩下所有边界框的 iou 并剔除那些 iou 值高于阈值的边界框,重复上述步骤;
# 流程1: 判断边界框的数目是否大于0
while len(cls_bboxes) > 0:
    # 流程2: 按照 socre 排序选出评分最大的边界框 A
    max_ind = np.argmax(cls_bboxes[:, 4])
    # 将边界框 A 取出并剔除
    best_bbox = cls_bboxes[max_ind]
    best_bboxes.append(best_bbox)
    cls_bboxes = np.concatenate([cls_bboxes[: max_ind], cls_bboxes[max_ind + 1:]])
	# 流程3: 计算这个边界框 A 与剩下所有边界框的 iou 并剔除那些 iou 值高于阈值的边界框
    iou = bboxes_iou(best_bbox[np.newaxis, :4], cls_bboxes[:, :4])
    weight = np.ones((len(iou),), dtype=np.float32)
    iou_mask = iou > iou_threshold
    weight[iou_mask] = 0.0
    cls_bboxes[:, 4] = cls_bboxes[:, 4] * weight
    score_mask = cls_bboxes[:, 4] > 0.
    cls_bboxes = cls_bboxes[score_mask]

最后所有取出来的边界框 A 就是我们想要的。不妨举个简单的例子:假如5个边界框及评分为: A: 0.9,B: 0.08,C: 0.8, D: 0.6,E: 0.5,设定的评分阈值为 0.3,计算步骤如下。

  • 步骤1: 边界框的个数为5,满足迭代条件;
  • 步骤2: 按照 socre 排序选出评分最大的边界框 A 并取出;
  • 步骤3: 计算边界框 A 与其他 4 个边界框的 iou,假设得到的 iou 值为:B: 0.1,C: 0.7, D: 0.02, E: 0.09, 剔除边界框 C;
  • 步骤4: 现在只剩下边界框 B、D、E,满足迭代条件;
  • 步骤5: 按照 socre 排序选出评分最大的边界框 D 并取出;
  • 步骤6: 计算边界框 D 与其他 2 个边界框的 iou,假设得到的 iou 值为:B: 0.06,E: 0.8,剔除边界框 E;
  • 步骤7: 现在只剩下边界框 B,满足迭代条件;
  • 步骤8: 按照 socre 排序选出评分最大的边界框 B 并取出;
  • 步骤9: 此时边界框的个数为零,结束迭代。

最后我们得到了边界框 A、B、D,但其中边界框 B 的评分非常低,这表明该边界框是没有物体的,因此应当抛弃掉。在 postprocess_boxes 代码中,

# # (5) discard some boxes with low scores
classes = np.argmax(pred_prob, axis=-1)
scores = pred_conf * pred_prob[np.arange(len(pred_coor)), classes]
score_mask = scores > score_threshold

在 YOLO 算法中,NMS 的处理有两种情况:一种是所有的预测框一起做 NMS 处理,另一种情况是分别对每个类别的预测框做 NMS 处理。后者会出现一个预测框既属于类别 A 又属于类别 B 的现象,这比较适合于一个小单元格中同时存在多个物体的情况。


补充

为了更好地理解2.4边界框的预测的xy_grid网格和pred_xy输出,举个简单的例子,3×3网格

output_size = 3 #假设输出的网格为3
batch_size = 1
anchor_per_scale = 3
y = tf.tile(tf.range(output_size, dtype=tf.int32)[:, tf.newaxis], [1, output_size])
x = tf.tile(tf.range(output_size, dtype=tf.int32)[tf.newaxis, :], [output_size, 1])
xy_grid = tf.concat([x[:, :, tf.newaxis], y[:, :, tf.newaxis]], axis=-1)
xy_grid1 = tf.tile(xy_grid[tf.newaxis, :, :, tf.newaxis, :], [batch_size, 1, 1, anchor_per_scale, 1])
xy_grid2 = tf.cast(xy_grid1, tf.float64)

sess = tf.Session()
print('y:n',sess.run(y))
print('x:n',sess.run(x))
print('xy_grid:n',sess.run(xy_grid))
print('xy_grid1:n',sess.run(xy_grid1))

xy_grid网格输出:

y:              x:            xy_grid:
 [[0 0 0]        [[0 1 2]            [[[0 0]     [[0 1]      [[0 2]
 [1 1 1]         [0 1 2]              [1 0]       [1 1]       [1 2]
 [2 2 2]]        [0 1 2]]             [2 0]]      [2 1]]      [2 2]]]

pred_xy的计算:

conv_output = np.ones([1,3,3,1,25])
conv_output[:, :, :, :, 0] = 1     # 假设偏移量dx = 1
conv_output[:, :, :, :, 1] = 2     # 假设偏移量dy = 2
conv_raw_dxdy = conv_output[:, :, :, :, 0:2]
pred_xy = (tf.sigmoid(conv_raw_dxdy) + xy_grid2) * stride
print('pred_xy:n',sess.run(pred_xy))

pred_xy输出:

[[[[[23.39387452 28.1855065 ]]        [[[23.39387452 60.1855065 ]]         [[[23.39387452 92.1855065 ]]
 
   [[55.39387452 28.1855065 ]]         [[55.39387452 60.1855065 ]]          [[55.39387452 92.1855065 ]]

   [[87.39387452 28.1855065 ]]]        [[87.39387452 60.1855065 ]]]         [[87.39387452 92.1855065 ]]]]]

可以看出pred_xy对每一个网格都有1个坐标输出

tf.pad函数的用法:

TensorFlow填充张量函数:tf.pad_w3cschool​www.w3cschool.cn
7ce146e806f0e79cfcd4f73a13119bf8.png

tf.image.resize_nearest_neighbor函数的用法:

TensorFlow函数:tf.image.resize_nearest_neighbor_w3cschool​www.w3cschool.cn
7ce146e806f0e79cfcd4f73a13119bf8.png

tf.layers.conv2d_transpose函数的用法:

TensorFlow函数:tf.layers.conv2d_transpose_w3cschool​www.w3cschool.cn
7ce146e806f0e79cfcd4f73a13119bf8.png

tf.concat函数用法:

将TensorFlow张量沿一个维度串联_w3cschool​www.w3cschool.cn
7ce146e806f0e79cfcd4f73a13119bf8.png

参考文章:

https://blog.csdn.net/leviopku/article/details/82660381​blog.csdn.net

谢谢观看,觉得好就点个赞呗!

引用\[1\]:错误信息:FileNotFoundError: \[Errno 2\] No such file or directory: '../AutoFrame/temp/report.xlsx' 相对于当前文件夹的路径,其实就是你写的py文件所在的文件夹路径! python在对文件的操作时,需要特别注意文件地址的书写。文件的路径地址,需要相对于正在被执行的.py文件来说。 举例说明: 1、正在执行.py文件在最外层(运行run_main.py),被操作的文件在其他文件夹时 report_B文件地址写成:./applicatiom/A/B/report_B.xlsx report_C文件地址写成:./applicatiom/A/C/report_C.xlsx report_temp文件地址写成:./temp/report_temp.xlsx 2、正在执行.py文件在文件夹中(运行test_01.py),被操作的文件在文件夹下时 report_B文件地址写成:./B/report_B.xlsx report_C文件地址写成:./C/report_C.xlsx 3、正在执行.py文件在文件夹中(运行test_01.py),被操作的文件在其他文件夹下时 report_temp.xlsx文件地址写成:../../temp/report_temp.xlsx report.xlsx文件地址写成:../../report.xlsx 注意事项:对多个文件进行操作时,可执行文件.py文件一定要固定目录,否则需要改动文件路径,否则找不到文件 运行run_main.py,操作../../temp/report_temp.xlsx文件路径,出现报错: raise FileCreateError(e) xlsxwriter.exceptions.FileCreateError: \[Errno 2\] No such file or directory: '../../temp/report_temp.xlsx'。 引用\[2\]:路径引用错误,原路径是…/data/,使用的是相对路径 解决方案: 改为正确的相对路径,在vscode中复制正确的你想引用的文件的相对路径临时却简单的方式:更改为绝对路径 f = gzip.open('E:/mlnum/neural-networks-and-deep-learning/data/mnist.pkl.gz', 'rb')。 问题: FileNotFoundError: \[Errno 2\] No such file or directory: 'E:\\ZJU_study\\YOLO\\mmyolo-main\\mmyolo-main\\configs\\custom_dataset\\../yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py' 回答: 这个错误是由于文件路径引用错误导致的。在你的代码中,文件路径'E:\\ZJU_study\\YOLO\\mmyolo-main\\mmyolo-main\\configs\\custom_dataset\\../yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py'无法找到。请确保你的文件路径是正确的,并且相对于当前执行的.py文件来说是有效的。你可以使用绝对路径或者正确的相对路径来引用文件。 #### 引用[.reference_title] - *1* [Python之FileNotFoundError: [Errno 2] No such file or directory问题处理](https://blog.csdn.net/qq_41845402/article/details/128974839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [FileNotFoundError: [Errno 2] No such file or directory](https://blog.csdn.net/kuake11/article/details/126111213)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值