配置文件详解

  • 四项基础配置
    • 1. _base_/datasets
    • 2. _base_/models
    • 3. _base_/schedules
    • 4. _base_/default_runtime.py
  • 基于基础配置的完整配置
    • 1. 继承
    • 2. 继承并修改
    • 3. 忽略基础配置的某些字段
  • 其它(关于源码)
    • 1. mask标注信息的读取
    • 2. 数据类别的加载逻辑

本文不涉及mmdetection的环境安装,主要记录一下自己在学习mmdetection时的心得体会。如有错误,欢迎大家在评论区指正。


四项基础配置

config/_base_ 文件夹下有 4 个基本组件类型,分别是:数据集(dataset),模型(model),训练策略(schedule)和运行时的默认设置(default runtime),官网称它们为原始配置(primitive)。

1. base/datasets

mmdetection支持 COCO、PASCAL VOC等常用的数据集,以 coco_instance.py为例:

dataset_type = 'CocoDataset' # 数据集类型
data_root = 'data/coco/' # 数据集的根路径
img_norm_cfg = dict(    # 图像归一化的配置mean=[123.675, 116.28, 103.53], # 归一化后数据的均值std=[58.395, 57.12, 57.375], to_rgb=True) # 归一化后数据集的标准差
train_pipeline = [ # 训练集图像的处理流程dict(type='LoadImageFromFile'), # 1. 从文件路径中加载数据dict(type='LoadAnnotations', # 2. 加载注解with_bbox=True, # 是否加载标注框(bounding box), 目标检测需要设置为 True。with_mask=True, # 是否加载mask信息,实例分割需要设置为 True。),dict(type='Resize', # 3. 缩放图像img_scale=(1333, 800), # 图像的最大尺寸keep_ratio=True # 是否保持图像的长宽比。),dict(type='RandomFlip', # 4. 随机翻转图像和注解flip_ratio=0.5 # 翻转概率),dict(type='Normalize', # 5. 归一化图像**img_norm_cfg # 上述的配置),dict(type='Pad', # 6. 填充当前图像到指定大小size_divisor=32 # 要求填充后图像可以被32整除),dict(type='DefaultFormatBundle'), # 7. 最后转换成的数据格式,具体描述见 ./mmdet/pipelines/formatting.pydict(type='Collect', # 8. 决定数据中哪些键应该传递给检测器keys=['img', 'gt_bboxes', 'gt_labels', 'gt_masks']  # 把原图、边界框、边界框的标签、语义信息传递给检测器),
]
test_pipeline = [ # 测试集图像的处理流程dict(type='LoadImageFromFile'), # 1. 从文件中加载数据dict(type='MultiScaleFlipAug', # 2. 数据增广:多尺度翻转增强img_scale=(1333, 800), # 测试时可图像的最大规模flip=False, # 是否翻转图像transforms=[    # 增强方式dict(type='Resize', # 缩放keep_ratio=True # 是否保持宽高比),dict(type='RandomFlip'), # 随机翻转,  flip=False 时不反转dict(type='Normalize', **img_norm_cfg), # 归一化配置项dict(type='Pad', size_divisor=32), # 图像尺寸padding到32的倍数dict(type='ImageToTensor', keys=['img']), # 将图像转为张量dict(type='Collect', keys=['img']), # 将原图传递给检测器])
]
data = dict(samples_per_gpu=2, # 单个 GPU 的 Batch sizeworkers_per_gpu=2, # 单个 GPU 分配的数据加载线程数,(为加快数据集的加载速度,一般会使用多线程加载)train=dict( # 训练数据集配置type=dataset_type, # 数据集类别, 这里是COCOann_file=data_root + 'annotations/instances_train2017.json', # 训练集的注解路径img_prefix=data_root + 'train2017/', # 训练集的图片路径pipeline=train_pipeline), # 训练时的数据增强流程val=dict(type=dataset_type, # 数据集类别, 这里是COCOann_file=data_root + 'annotations/instances_val2017.json', # 验证集的注解路径img_prefix=data_root + 'val2017/', # 验证集的图片路径pipeline=test_pipeline), # 验证时的数据增强流程test=dict(type=dataset_type, # 数据集类别, 这里是COCOann_file=data_root + 'annotations/instances_val2017.json', # 测试集的注解路径img_prefix=data_root + 'val2017/', # 测试集的图片路径pipeline=test_pipeline)) # 测试时的数据增强流程
evaluation = dict( # 评估设置interval=1, # 验证的间隔metric=['bbox', 'segm'] # 验证时使用的评价指标
)
  • dataset_type :官方提供了多种格式的数据集加载器,除了Coco、VOC外,同时还提供了类平衡数据加载等。
  • 支持用户编写自定义的数据集加载器,继承 CustomDataset ,并重写 load_annotations(self, ann_file) 以及 get_ann_info(self, idx) 这两个方法。
  • 数据集加载器中已经声明了数据类别,如果训练自己的数据集,有两种方案。
    • 方式一
      可以直接在 mmdet/datasets下修改类别,如 mmdet/datasets/coco.py

      @DATASETS.register_module()
      class CocoDataset(CustomDataset):CLASSES = ('person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus','train', 'truck', 'boat', 'traffic light', 'fire hydrant','stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog','horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe','backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee','skis', 'snowboard', 'sports ball', 'kite', 'baseball bat','baseball glove', 'skateboard', 'surfboard', 'tennis racket','bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl','banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot','hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch','potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop','mouse', 'remote', 'keyboard', 'cell phone', 'microwave','oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock','vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush')
      
    • 方式二
      在配置文件中设置类别,

      classes = ('person', 'bicycle', 'car')
      data = dict(
      train=dict(classes=classes),
      val=dict(classes=classes),
      test=dict(classes=classes))
      

      也可以根据文件读取类别。

      classes = 'path/to/classes.txt'
      data = dict(
      train=dict(classes=classes),
      val=dict(classes=classes),
      test=dict(classes=classes))
      

      classes.txt的格式如下:

      person
      bicycle
      car
      

2. base/models

该路径下提供了常用的目标检测模型,如Fast R-CNN、 Faster R-CNN、RetinaNet、SSD300等,也有实例分割模型Mask R-CNN,这里以Mask R-CNN的配置为例进行介绍

Mask R-CNN的大致流程为:

  1. image输入网络后,主干网络进行特征提取
  2. neck部分使用特征金字塔(FPN)进行增强特征提取
  3. RPN网络根据FPN的输出生成一定数量的推荐框
  4. RoI Align根据推荐框映射生成尺寸为7×7的特征图
  5. 根据RoI Align的输出(7×7的特征图)进行分类和回归计算,得到目标框
  6. RoI Align根据目标框映射生成尺寸为14×14的特征图
  7. 14×14的特征图经过全卷积网络输出28×28的语义分割结果
  8. 语义分割结果缩放到与目标框相同的大小,得到最终的实例分割结果
# model settings
model = dict(type='MaskRCNN',    # wang'lbackbone=dict(  # 主干网络设置type='ResNet', # 主干网络名称depth=50, # 层数,resnet有res50、res101、res152num_stages=4,   # 输出四个阶段的特征图out_indices=(0, 1, 2, 3), # 四个阶段的indexfrozen_stages=1, # 冻结第一个阶段norm_cfg=dict(type='BN',  # 归一化层(norm layer)的配置项requires_grad=True # 是否训练归一化里的 gamma 和 beta。),   #norm_eval=True, # 是否冻结 BN 里的统计项。style='pytorch', # 主干网络的风格,有pytorch和caffe两种init_cfg=dict( # 初始化设置type='Pretrained',  # 适用于预训练模型checkpoint='torchvision://resnet50' #)), #neck=dict(  # 检测器的颈部type='FPN', # 使用特征金字塔in_channels=[256, 512, 1024, 2048], # neck的输入,对应主干网络输出的特征图的通道数out_channels=256, # 特征金字塔的输出的通道数num_outs=5 # 输出的通道数),rpn_head=dict( # RPN网络type='RPNHead', # 网络类型in_channels=256, # 每个输入特征图的输入通道,这与 neck 的输出通道一致。feat_channels=256, # head 卷积层的特征通道anchor_generator=dict( # 锚框生成相关的配置type='AnchorGenerator', # 大多是方法使用 AnchorGenerator 作为锚点生成器, SSD 检测器使用 `SSDAnchorGenerator`scales=[8], # 锚点的基本比例,特征图某一位置的锚点面积为 scale * base_sizesratios=[0.5, 1.0, 2.0], # 锚框的宽高比strides=[4, 8, 16, 32, 64]), # 锚生成器的步幅。这与 FPN 特征步幅一致bbox_coder=dict( # RPN需要对建议框进行编码和解码。type='DeltaXYWHBBoxCoder', # 框编码器的类别target_means=[.0, .0, .0, .0], # 用于编码和解码框的目标均值target_stds=[1.0, 1.0, 1.0, 1.0]), # 用于编码和解码框的标准差loss_cls=dict( # RPN分类分支的损失函数type='CrossEntropyLoss',  # 使用交叉熵损失use_sigmoid=True,  # RPN通常进行二分类,所以通常使用sigmoid函数loss_weight=1.0), # 分类分支的损失权重loss_bbox=dict( # 回归分支的损失函数配置type='L1Loss',  # 使用L1损失loss_weight=1.0 # 回归分支的损失权重)),roi_head=dict( # RoI,包括了特征映射以及Mask R-CNN需要的分类分支、回归分支、mask分支type='StandardRoIHead', # 使用标准的RoI head 的类型bbox_roi_extractor=dict( # 用于目标检测预测的 RoI 特征提取器type='SingleRoIExtractor', # RoI 特征提取器的类型roi_layer=dict(type='RoIAlign',  # RoI 层的类别output_size=7,     # RoI输出的特征图大小sampling_ratio=0), # 提取 RoI 特征时的采样率。0 表示自适应比率out_channels=256, # 输出的通道数featmap_strides=[4, 8, 16, 32]), # 多尺度特征图的步幅,应该与主干的架构保持一致bbox_head=dict( # RoIHead 中 box head 的配置type='Shared2FCBBoxHead',   # 两个权值共享的全连接层用于预测框回归in_channels=256,  # 输入通道数,对应bbox_roi_extractor的输出通道数fc_out_channels=1024,   # 全连接层的输出通道roi_feat_size=7,    # RoI输出的特征图大小num_classes=80, # 预测的类别数bbox_coder=dict( # 预测框编码type='DeltaXYWHBBoxCoder',target_means=[0., 0., 0., 0.],target_stds=[0.1, 0.1, 0.2, 0.2]),reg_class_agnostic=False,   # 回归是否与类别无关loss_cls=dict(  #  # 分类分支的损失函数配置type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),loss_bbox=dict(type='L1Loss', loss_weight=1.0)),mask_roi_extractor=dict( # 用于mask分支的  RoI 特征提取器type='SingleRoIExtractor',roi_layer=dict(type='RoIAlign', output_size=14, sampling_ratio=0),out_channels=256,featmap_strides=[4, 8, 16, 32]),mask_head=dict( # mask分支type='FCNMaskHead', # 使用全卷积网络进行预测num_convs=4,in_channels=256,conv_out_channels=256,num_classes=80,loss_mask=dict( # mask分支的损失函数type='CrossEntropyLoss', use_mask=True, loss_weight=1.0))),# model training and testing settingstrain_cfg=dict( # 训练是的超参数配置rpn=dict( # RPN网络的超参数assigner=dict( # 分配器,即区分正负样本type='MaxIoUAssigner',pos_iou_thr=0.7, # IoU >= 0.7(阈值) 被视为正样本。neg_iou_thr=0.3, # IoU < 0.3(阈值) 被视为负样本min_pos_iou=0.3, # 框作为正样本的最小 IoU 阈值match_low_quality=True,ignore_iof_thr=-1),sampler=dict( # 正/负采样器(sampler)的配置type='RandomSampler',num=256,pos_fraction=0.5, # 正样本占总样本的比例。neg_pos_ub=-1, # 基于正样本数量的负样本上限add_gt_as_proposals=False), # 采样后是否添加 GT 作为 proposalallowed_border=-1,pos_weight=-1, # 训练期间正样本的权重debug=False),rpn_proposal=dict( # 在训练期间生成 proposals 的配置nms_pre=2000,   # NMS 前的 box 数max_per_img=1000, # NMS 要保留的 box 的数量nms=dict(   # NMS 的配置type='nms',iou_threshold=0.7),min_bbox_size=0 # # 允许的最小 box 尺寸),rcnn=dict(assigner=dict(type='MaxIoUAssigner',pos_iou_thr=0.5,neg_iou_thr=0.5,min_pos_iou=0.5,match_low_quality=True,ignore_iof_thr=-1),sampler=dict(type='RandomSampler',num=512,pos_fraction=0.25,neg_pos_ub=-1,add_gt_as_proposals=True),mask_size=28, # mask 的大小pos_weight=-1,debug=False)),test_cfg=dict(rpn=dict(nms_pre=1000,max_per_img=1000,nms=dict(type='nms', iou_threshold=0.7),min_bbox_size=0),rcnn=dict(score_thr=0.05,nms=dict(type='nms', iou_threshold=0.5),max_per_img=100,    # 每张图像的最大检测次数, 对应nms输出的最大个数mask_thr_binary=0.5)))
  • 一般的目标检测的模型可以分为backbonde、neck、head三个部分
  • Mask R-CNN的配置项比较多,主要有
    • 主干提取(backbone)的配置
    • 增强特征提取(neck)的配置
    • RPN_head:RPN网络负责生成建议框,因此包含锚框生成、建议框分类分支、建议框的回归分支
    • RoI_head:负责对建议框进行修正,包括感兴趣区域对齐(RoI Align),分类分支、回归分支、mask分支。

3. base/schedules

该路径下配置训练计划,如训练使用的优化器、学习率的调整策略等
以schedule_1x.py为例

# optimizer
optimizer = dict(type='SGD',    # 优化器种类lr=0.02,       # 优化器的学习率momentum=0.9,   # 动量(Momentum)weight_decay=0.0001 # SGD 的权重衰减
)
optimizer_config = dict(grad_clip=None) # # 大多数方法不使用梯度限制(grad_clip)
# learning policy
lr_config = dict(   # 学习率调整配置policy='step',  # 调度流程(scheduler)的策略warmup='linear',    # 预热(warmup)策略warmup_iters=500,   # 预热的迭代次数warmup_ratio=0.001,  # 用于预热的起始学习率的比率step=[8, 11])   # 衰减学习率的起止回合数
runner = dict(type='EpochBasedRunner',    # runner 的类别,有 EpochBasedRunner 和 IterBasedRunner 两种max_epochs=12) # 训练的迭代数
  • 优化器可以自行选择,有SGD、Adam、AdamW等

    • 好像是使用的pytorch里的优化器,可以看mmcv里的代码 mmcv.runner.base_runner
  • 所谓的1x,2x,3x是指epoch的大小,1x = 12个epoch。这里有一些官网的解释:COCO数据集上1x模式下为什么不采用多尺度训练

4. base/default_runtime.py

该文件配置一些程序运行时的参数

checkpoint_config = dict(interval=1, # 训练过程中保存权重的间隔by_epoch=True, # interval的计数单位默认是epoch,设置为False则计数单位为iter
)
# yapf:disable
log_config = dict(  # 日志配置interval=50,    # 打印日志的间隔hooks=[     #dict(type='TextLoggerHook'),# dict(type='TensorboardLoggerHook')])
# yapf:enable
custom_hooks = [dict(type='NumClassCheckHook')]dist_params = dict(backend='nccl')
log_level = 'INFO'      # 日志的级别。
load_from = None    # 从一个给定路径里加载模型作为预训练模型
resume_from = None  # 从给定路径里恢复检查点(checkpoints),训练模式将从检查点保存的轮次开始恢复训练
workflow = [('train', 1)]   # runner 的工作流程,[('train', 1)] 表示只有一个工作流且工作流仅执行一次。根据 total_epochs 工作流训练 12个回合。# disable opencv multithreading to avoid system being overloaded
opencv_num_threads = 0
# set multi-process start method as `fork` to speed up the training
mp_start_method = 'fork'

基于基础配置的完整配置

1. 继承

一个完整的配置文件包括上述的四部分配置,因此可以直接继承上述配置文件

  • mask_rcnn_r50_fpn_1x_coco.py

    _base_ = ['../_base_/models/mask_rcnn_r50_fpn.py','../_base_/datasets/coco_instance.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
    ]
    

2. 继承并修改

也可基于配置文件进行一定的修改,如将主干网络从res50替换为res101

  • mask_rcnn_r101_fpn_1x_coco.py

    _base_ = ['../_base_/models/mask_rcnn_r50_fpn.py','../_base_/datasets/coco_instance.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
    ]
    model = dict(backbone=dict(depth=101,init_cfg=dict(type='Pretrained',checkpoint='torchvision://resnet101')))
    

3. 忽略基础配置的某些字段

如果需要完全修改某一个配置项,如优化器,可以先删除原始配置,再添加新的配置,如使用新的优化器,此时需要使用 _delete_=True

  • _delete_=True 表示删除原有的配置。如果不删除原有的配置项,SGDmomentum=0.9 配置项会作为参数传递给优化器的建造器,但是 AdamW 不需要该参数,此时会发生报错。
  • mask_rcnn_r50_fpn_1x_coco.py 使用AdamW 优化器
    _base_ = ['../_base_/models/mask_rcnn_r50_fpn.py','../_base_/datasets/coco_instance.py','../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
    ]
    optimizer = dict(_delete_=True,type='AdamW',lr=0.0001,betas=(0.9, 0.999),weight_decay=0.05,paramwise_cfg=dict(custom_keys={'absolute_pos_embed': dict(decay_mult=0.),'relative_position_bias_table': dict(decay_mult=0.),'norm': dict(decay_mult=0.)}))
    

其它(关于源码)

该部分是一些关于源码的理解

1. mask标注信息的读取

mmdet/datasets/voc.py 没有读取mask信息,但是 mmdet/datasets/coco.py 中读取了语义标注。所以如果使用voc格式需要跑实例分割的话,要么转换成COCO格式,要么重写一个voc的数据加载器。

  • voc.py 没有重写 mmdet/datasets/get_ann_info(self, idx): ,它继承自 mmdet/datasets/xml_style.py ,所以直接看 xml_style.py 的代码。

  • mmdet/datasets/xml_style.py

    @DATASETS.register_module()
    class XMLDataset(CustomDataset):...def get_ann_info(self, idx):...# 此处没有读取mask信息ann = dict(bboxes=bboxes.astype(np.float32),labels=labels.astype(np.int64),bboxes_ignore=bboxes_ignore.astype(np.float32),labels_ignore=labels_ignore.astype(np.int64))return ann
    
  • mmdet/datasets/coco.py

    @DATASETS.register_module()
    class CocoDataset(CustomDataset):def get_ann_info(self, idx):img_id = self.data_infos[idx]['id']ann_ids = self.coco.get_ann_ids(img_ids=[img_id])ann_info = self.coco.load_anns(ann_ids)return self._parse_ann_info(self.data_infos[idx], ann_info)def _parse_ann_info(self, img_info, ann_info):gt_masks_ann = []for i, ann in enumerate(ann_info):if ann.get('ignore', False):continuex1, y1, w, h = ann['bbox']inter_w = max(0, min(x1 + w, img_info['width']) - max(x1, 0))inter_h = max(0, min(y1 + h, img_info['height']) - max(y1, 0))if inter_w * inter_h == 0:continueif ann['area'] <= 0 or w < 1 or h < 1:continueif ann['category_id'] not in self.cat_ids:continuebbox = [x1, y1, x1 + w, y1 + h]if ann.get('iscrowd', False):gt_bboxes_ignore.append(bbox)else:gt_bboxes.append(bbox)gt_labels.append(self.cat2label[ann['category_id']])gt_masks_ann.append(ann.get('segmentation', None))...# 读取了mask信息ann = dict(bboxes=gt_bboxes,labels=gt_labels,bboxes_ignore=gt_bboxes_ignore,masks=gt_masks_ann,seg_map=seg_map)return ann
    

2. 数据类别的加载逻辑

CustomDataset是CocoDataset与VOCDataset的父类,它的源码在 mmdet/datasets/custom.py

  • mmdet/datasets/custom.py

    @DATASETS.register_module()
    class CustomDataset(Dataset):def __init__(self,ann_file,pipeline,classes=None,data_root=None,img_prefix='',seg_prefix=None,proposal_file=None,test_mode=False,filter_empty_gt=True,file_client_args=dict(backend='disk')):...self.CLASSES = self.get_classes(classes)...@classmethoddef get_classes(cls, classes=None):# 如果配置文件中没有classes,则使用类内默认的CLASSESif classes is None:return cls.CLASSES# 如果classes是字符串,则从文件中读取类别if isinstance(classes, str):# take it as a file pathclass_names = mmcv.list_from_file(classes)# 如果classes是元组或列表,则直接赋值给classeselif isinstance(classes, (tuple, list)):class_names = classeselse:raise ValueError(f'Unsupported type{type(classes)}of classes.')return class_names
    

mmdetection学习笔记(二)配置文件详解相关推荐

  1. Hibernate学习笔记--映射配置文件详解

    参考资料: http://blog.163.com/hzd_love/blog/static/13199988120108265317988/ http://www.cnblogs.com/often ...

  2. ELK学习笔记之Logstash详解

    0x00 Logstash概述 官方介绍:Logstash is an open source data collection engine with real-time pipelining cap ...

  3. Java中大数据数组,Java基础学习笔记之数组详解

    摘要:这篇Java开发技术栏目下的"Java基础学习笔记之数组详解",介绍的技术点是"java基础学习笔记.基础学习笔记.Java基础.数组详解.学习笔记.Java&qu ...

  4. oracle scn 重置,学习笔记:Oracle SCN详解 SCN与Oracle数据库恢复的关系

    天萃荷净 分享一篇关于Oracle SCN的详解,介绍SCN与Oracle数据库恢复的关系和SCN在数据库中的作用 一.为什么需要System checkpoint SCN号与Datafile Che ...

  5. expect学习笔记及实例详解【转】

    1. expect是基于tcl演变而来的,所以很多语法和tcl类似,基本的语法如下所示: 1.1 首行加上/usr/bin/expect 1.2 spawn: 后面加上需要执行的shell命令,比如说 ...

  6. Ruoyi框架学习--Vue前端配置文件详解

    CSDN话题挑战赛第2期 参赛话题:学习笔记 1.package.json配置中的重点介绍: 1.许可证:license:各种License介绍(BSD,MIT,MPL,Apache License, ...

  7. PyQt5学习笔记——一文详解QObject

    QObject详解笔记1 一.简介 QObject是所有Qt对象的基类 二.功能作用 2.1 对象名称.属性 2.1.1 API setObjectName("唯一名称") 给QT ...

  8. shell入门学习笔记-12-命令详解: echo与printf

    系列目录与参考文献传送门: shell入门学习笔记-序章 命令详解 admindeMacBook-Pro:myshell admin$ type cd cd is a shell builtin ad ...

  9. CoAP学习笔记——CoAP格式详解

    0 前言 CoAP是受限制的应用协议(Constrained Application Protocol)的代名词.在当前由PC机组成的世界,信息交换是通过TCP和应用层协议HTTP实现的.但是对于小型 ...

  10. 【Python全栈100天学习笔记】Day37MySQL详解(sql语句基本操作含索引、视图、存储过程)

    SQL详解 基本操作 我们通常可以将SQL分为三类:DDL(数据定义语言).DML(数据操作语言)和DCL(数据控制语言).DDL主要用于创建(create).删除(drop).修改(alter)数据 ...

最新文章

  1. iPhone开发的一些小技巧
  2. 应用程序 mysql 连接_学生信息管理系统之四:实现应用程序与数据库的连接
  3. 线上Haproxy配置
  4. 疯狂吸金1600个亿的拼多多,社交网络分析到底隐藏多少财富?
  5. 6款国内外SNS开源软件 搭建社交网站利器
  6. sprint boot_Sprint Boot子战争包装
  7. 【Kaggle】Intermediate Machine Learning(管道+交叉验证)
  8. java mq发送sdk_【转载】java实现rabbitmq消息的发送接受
  9. java打印正三角形_java实现打印正三角的方法
  10. CListCtrl 使用方法总结
  11. Bootstrap图片中加播放按钮
  12. Atitit java onvif 开源类库 getProfiles getStreamUri
  13. 小爱音箱mini系统故障怎么办_Win7系统连接小爱音箱mini的方法【图文】
  14. USB转串口,JLINK驱动安装(亲测有效)
  15. 台大林轩田《机器学习基石》:作业三python实现
  16. 智能车仿真 —— 室外光电创意组(racecar车模仿真,阿克曼车辆模型)
  17. 软件考试(一)计算机技术与软件专业技术资格(水平)考试(以下简称软件水平考试)含金量最高,与职称挂钩。
  18. 5A成绩通过PMP,备考经验总结——姜飞
  19. JavaScript系列(2)内置对象:Date
  20. SpringBoot安全验证之Referer拦截器

热门文章

  1. 还没通关“猜画小歌”?击败谷歌AI的秘籍在此
  2. MultiDex原理分析
  3. Jutoh for Mac(电子书制作工具)
  4. 数据库中 码、主码、候选码的辨别
  5. 使用姿态描述子与粗糙集理论
  6. 安卓虚拟机_安卓手机上面玩虚拟机?这才是真正的玩机神器
  7. 博客-评论系统数据库设计及实现
  8. 小说阅读Autojs源码学习
  9. Split Game
  10. 二十一世纪大学英语读写教程(第四册)学习笔记(原文)——6 - The EQ Factor(情商因素)