├── .gitignore ├── 第01讲 介绍 ├── assets │ ├── 1.png │ ├── 2.png │ └── 3.png └── README.md ├── 第07讲 实战:手写数字识别 ├── 7.png ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── test.py ├── model.py ├── train.py └── README.md ├── 第03讲 环境搭建 ├── assets │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── README.md ├── 第06讲 常用的运算操作 ├── assets │ ├── 1.gif │ ├── 2.gif │ ├── 3.gif │ ├── 4.gif │ ├── 5.png │ ├── 6.png │ ├── 7.jpg │ ├── 8.jpg │ ├── 9.png │ ├── 10.png │ └── 11.png ├── test.py └── README.md ├── 第08讲 常用的卷积结构 ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── SE.py ├── README.md ├── Residual.py └── DSC.py ├── 第02讲 计算机视觉的分类 ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.gif └── README.md ├── 第04讲 深度学习的基本实现流程 ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.jpg │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png └── README.md ├── 第05讲 实战:PaddleX实现垃圾分类 ├── 188.jpg ├── assets │ └── 1.png ├── infer.py ├── rubbish │ ├── rename.py │ └── cls.py ├── train.py └── README.md ├── 第09讲 MobilenetV3网络详解 ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── PaddleClas-release-2.3 │ ├── 188.jpg │ ├── command.txt │ └── MobileNetV3_large_x1_0.yaml └── README.md ├── 第10讲 目标检测YOLOv3-backbone ├── assets │ ├── 1.png │ ├── 2.png │ ├── 3.jpg │ └── 4.png ├── darknet.py └── README.md ├── 第11讲 目标检测YOLOv3-neck、head ├── assets │ ├── 1.jpg │ ├── 2.png │ ├── 3.png │ └── 4.png ├── model.py ├── README.md ├── head.py ├── neck.py └── darknet.py ├── 第12讲 实战:PaddleDeteciton实现目标检测 ├── assets │ ├── 1.png │ ├── 2.jpg │ ├── 3.png │ └── 4.png ├── Paddledetection_tutorial │ └── configs │ │ ├── runtime.yml │ │ ├── yolov3 │ │ ├── _base_ │ │ │ ├── optimizer_270e.yml │ │ │ ├── yolov3_darknet53.yml │ │ │ └── yolov3_reader.yml │ │ └── yolov3_darknet53_270e_voc.yml │ │ └── datasets │ │ └── voc.yml └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | *.pdparams 3 | -------------------------------------------------------------------------------- /第01讲 介绍/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第01讲 介绍/assets/1.png -------------------------------------------------------------------------------- /第01讲 介绍/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第01讲 介绍/assets/2.png -------------------------------------------------------------------------------- /第01讲 介绍/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第01讲 介绍/assets/3.png -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第07讲 实战:手写数字识别/7.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/1.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/10.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/11.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/12.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/13.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/14.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/15.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/16.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/2.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/3.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/4.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/5.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/6.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/7.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/8.png -------------------------------------------------------------------------------- /第03讲 环境搭建/assets/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第03讲 环境搭建/assets/9.png -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/1.gif -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/2.gif -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/3.gif -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/4.gif -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/5.png -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/6.png -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/7.jpg -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/8.jpg -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/9.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/1.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/2.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/3.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/4.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/5.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/6.png -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第08讲 常用的卷积结构/assets/7.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/1.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/2.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/3.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/4.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/5.png -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/assets/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第02讲 计算机视觉的分类/assets/6.gif -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/10.png -------------------------------------------------------------------------------- /第06讲 常用的运算操作/assets/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第06讲 常用的运算操作/assets/11.png -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第07讲 实战:手写数字识别/assets/1.png -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第07讲 实战:手写数字识别/assets/2.png -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第07讲 实战:手写数字识别/assets/3.png -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第07讲 实战:手写数字识别/assets/4.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/1.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/2.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/3.jpg -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/4.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/5.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/6.png -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第04讲 深度学习的基本实现流程/assets/7.png -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/188.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第05讲 实战:PaddleX实现垃圾分类/188.jpg -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/1.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/2.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/3.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/4.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/5.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/6.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/assets/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/assets/7.png -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第05讲 实战:PaddleX实现垃圾分类/assets/1.png -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第10讲 目标检测YOLOv3-backbone/assets/1.png -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第10讲 目标检测YOLOv3-backbone/assets/2.png -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/assets/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第10讲 目标检测YOLOv3-backbone/assets/3.jpg -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第10讲 目标检测YOLOv3-backbone/assets/4.png -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/assets/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第11讲 目标检测YOLOv3-neck、head/assets/1.jpg -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第11讲 目标检测YOLOv3-neck、head/assets/2.png -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第11讲 目标检测YOLOv3-neck、head/assets/3.png -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第11讲 目标检测YOLOv3-neck、head/assets/4.png -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第12讲 实战:PaddleDeteciton实现目标检测/assets/1.png -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第12讲 实战:PaddleDeteciton实现目标检测/assets/2.jpg -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/assets/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第12讲 实战:PaddleDeteciton实现目标检测/assets/3.png -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第12讲 实战:PaddleDeteciton实现目标检测/assets/4.png -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/PaddleClas-release-2.3/188.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Monday-Leo/Paddle_tutorial/HEAD/第09讲 MobilenetV3网络详解/PaddleClas-release-2.3/188.jpg -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/runtime.yml: -------------------------------------------------------------------------------- 1 | use_gpu: true 2 | log_iter: 20 3 | save_dir: output 4 | snapshot_epoch: 1 5 | print_flops: false 6 | -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/infer.py: -------------------------------------------------------------------------------- 1 | import paddlex as pdx 2 | model = pdx.load_model('output/mobilenetv3_small/best_model') 3 | result = model.predict('188.jpg') 4 | print("Predict Result: ", result) -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/yolov3/_base_/optimizer_270e.yml: -------------------------------------------------------------------------------- 1 | epoch: 270 2 | 3 | LearningRate: 4 | base_lr: 0.001 5 | schedulers: 6 | - !PiecewiseDecay 7 | gamma: 0.1 8 | milestones: 9 | - 216 10 | - 243 11 | - !LinearWarmup 12 | start_factor: 0. 13 | steps: 4000 14 | 15 | OptimizerBuilder: 16 | optimizer: 17 | momentum: 0.9 18 | type: Momentum 19 | regularizer: 20 | factor: 0.0005 21 | type: L2 22 | -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/model.py: -------------------------------------------------------------------------------- 1 | from darknet import DarkNet 2 | from neck import YOLOv3FPN 3 | from head import YOLOv3Head 4 | import paddle 5 | 6 | inputs = paddle.rand((1,3,416,416)) 7 | backbone = DarkNet() 8 | neck = YOLOv3FPN() 9 | head = YOLOv3Head(num_classes=20) 10 | 11 | outs = backbone(inputs) 12 | for out in outs: 13 | print(out.shape) 14 | 15 | outs = neck(outs) 16 | for out in outs: 17 | print(out.shape) 18 | 19 | outs = head(outs) 20 | for out in outs: 21 | print(out.shape) -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/PaddleClas-release-2.3/command.txt: -------------------------------------------------------------------------------- 1 | python tools/train.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Arch.pretrained=True -o Global.device=gpu 2 | 3 | python tools/train.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Global.checkpoints="./output/MobileNetV3_large_x1_0/epoch_5" -o Global.device=gpu 4 | 5 | python tools/infer.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Infer.infer_imgs=./188.jpg -o Global.pretrained_model=./output/MobileNetV3_large_x1_0/best_model -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/yolov3/yolov3_darknet53_270e_voc.yml: -------------------------------------------------------------------------------- 1 | _BASE_: [ 2 | '../datasets/voc.yml', 3 | '../runtime.yml', 4 | '_base_/optimizer_270e.yml', 5 | '_base_/yolov3_darknet53.yml', 6 | '_base_/yolov3_reader.yml', 7 | ] 8 | 9 | snapshot_epoch: 5 10 | weights: output/yolov3_darknet53_270e_voc/model_final 11 | 12 | # set collate_batch to false because ground-truth info is needed 13 | # on voc dataset and should not collate data in batch when batch size 14 | # is larger than 1. 15 | EvalReader: 16 | collate_batch: false 17 | -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/datasets/voc.yml: -------------------------------------------------------------------------------- 1 | metric: VOC 2 | map_type: 11point 3 | num_classes: 20 4 | 5 | TrainDataset: 6 | !VOCDataSet 7 | dataset_dir: dataset/VOC2007 8 | anno_path: train.txt 9 | label_list: labels.txt 10 | data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult'] 11 | 12 | EvalDataset: 13 | !VOCDataSet 14 | dataset_dir: dataset/VOC2007 15 | anno_path: eval.txt 16 | label_list: labels.txt 17 | data_fields: ['image', 'gt_bbox', 'gt_class', 'difficult'] 18 | 19 | TestDataset: 20 | !ImageFolder 21 | anno_path: dataset/VOC2007/labels.txt 22 | -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import paddle 3 | from model import LeNet 4 | from paddle.vision.datasets import MNIST 5 | from paddle.vision.transforms import ToTensor 6 | import paddle.nn.functional as F 7 | 8 | valid_loader = MNIST(mode='test', transform=ToTensor()) 9 | img = np.array(valid_loader[0][0]) #[1,28,28] 10 | 11 | # import matplotlib.pyplot as plt 12 | # plt.imshow(img.squeeze(), cmap='gray') 13 | # plt.show() 14 | 15 | model = LeNet(num_classes=10) 16 | model_dict = paddle.load("mnist.pdparams") 17 | model.set_state_dict(model_dict) 18 | model.eval() 19 | x = valid_loader[0][0].reshape((1,1,28,28)).astype('float32') 20 | result = F.softmax(model(x)) 21 | print(result.numpy()[0]) -------------------------------------------------------------------------------- /第06讲 常用的运算操作/test.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | 5 | input = paddle.rand(shape=[1, 3, 32, 32]) 6 | 7 | Conv2D = nn.Conv2D(in_channels=3,out_channels=16,kernel_size=2,stride=2) 8 | output = Conv2D(input) 9 | print("Conv2D",output.shape) 10 | 11 | MaxPool2D = nn.MaxPool2D(kernel_size=2,stride=2,padding=0) 12 | output = MaxPool2D(input) 13 | print("MaxPool2D",output.shape) 14 | 15 | AvgPool2D = nn.AvgPool2D(kernel_size=2,stride=2,padding=0) 16 | output = AvgPool2D(input) 17 | print("AvgPool2D",output.shape) 18 | 19 | batch_norm = nn.BatchNorm(num_channels=3) 20 | output = batch_norm(input) 21 | print("batch_norm",output.shape) 22 | 23 | x = paddle.to_tensor([-2.0,-1.0,0.0,1.0,2.0]) 24 | output = F.relu(x) 25 | print("relu",output) 26 | 27 | -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/rubbish/rename.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def rename(): 4 | res = os.listdir('./') 5 | for a in res: 6 | i = 0 7 | flag = os.path.isdir(a) 8 | if(flag == False): 9 | continue 10 | path=a 11 | filelist=os.listdir(path)#该文件夹下所有的文件(包括文件夹) 12 | for files in filelist:#遍历所有文件 13 | i=i+1 14 | Olddir=os.path.join(path,files);#原来的文件路径 15 | if os.path.isdir(Olddir):#如果是文件夹则跳过 16 | continue 17 | filename=os.path.splitext(files)[0];#文件名 18 | filetype=os.path.splitext(files)[1];#文件扩展名 19 | Newdir=os.path.join(path,str(i)+filetype);#新的文件路径 20 | os.rename(Olddir,Newdir)#重命名 21 | rename() 22 | -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/yolov3/_base_/yolov3_darknet53.yml: -------------------------------------------------------------------------------- 1 | architecture: YOLOv3 2 | pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/DarkNet53_pretrained.pdparams 3 | norm_type: sync_bn 4 | 5 | YOLOv3: 6 | backbone: DarkNet 7 | neck: YOLOv3FPN 8 | yolo_head: YOLOv3Head 9 | post_process: BBoxPostProcess 10 | 11 | DarkNet: 12 | depth: 53 13 | return_idx: [2, 3, 4] 14 | 15 | # use default config 16 | # YOLOv3FPN: 17 | 18 | YOLOv3Head: 19 | anchors: [[10, 13], [16, 30], [33, 23], 20 | [30, 61], [62, 45], [59, 119], 21 | [116, 90], [156, 198], [373, 326]] 22 | anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] 23 | loss: YOLOv3Loss 24 | 25 | YOLOv3Loss: 26 | ignore_thresh: 0.7 27 | downsample: [32, 16, 8] 28 | label_smooth: false 29 | 30 | BBoxPostProcess: 31 | decode: 32 | name: YOLOBox 33 | conf_thresh: 0.005 34 | downsample_ratio: 32 35 | clip_bbox: true 36 | nms: 37 | name: MultiClassNMS 38 | keep_top_k: 100 39 | score_threshold: 0.01 40 | nms_threshold: 0.45 41 | nms_top_k: 1000 42 | -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/train.py: -------------------------------------------------------------------------------- 1 | from paddlex import transforms as T 2 | import paddlex as pdx 3 | 4 | train_transforms = T.Compose([ 5 | T.RandomCrop(crop_size=224), 6 | T.RandomHorizontalFlip(), 7 | T.Normalize()]) 8 | 9 | eval_transforms = T.Compose([ 10 | T.ResizeByShort(short_size=256), 11 | T.CenterCrop(crop_size=224), 12 | T.Normalize() 13 | ]) 14 | 15 | train_dataset = pdx.datasets.ImageNet( 16 | data_dir='rubbish', 17 | file_list='rubbish/train.txt', 18 | label_list='rubbish/labels.txt', 19 | transforms=train_transforms, 20 | shuffle=True) 21 | eval_dataset = pdx.datasets.ImageNet( 22 | data_dir='rubbish', 23 | file_list='rubbish/eval.txt', 24 | label_list='rubbish/labels.txt', 25 | transforms=eval_transforms) 26 | 27 | num_classes = len(train_dataset.labels) 28 | model = pdx.cls.MobileNetV3_small(num_classes=num_classes) 29 | 30 | model.train(num_epochs=10, 31 | train_dataset=train_dataset, 32 | train_batch_size=64, 33 | eval_dataset=eval_dataset, 34 | lr_decay_epochs=[4, 6, 8], 35 | save_dir='output/mobilenetv3_small', 36 | use_vdl=True) -------------------------------------------------------------------------------- /第01讲 介绍/README.md: -------------------------------------------------------------------------------- 1 | ## 学习人工智能的意义 2 | 3 | 近些年来,人工智能技术迅速发展,已经在多个领域取得了实质性的突破,也有了众多工业内的应用,新能源汽车的**自动驾驶**、路面交通的**违章识别**、每日打卡的**人脸识别**、大数据的**智能化搜索**以及**文字的提取翻译**等等,都使用了人工智能技术。其中,计算机视觉是人工智能的重要组成部分,以往使用传统技术很难攻克的难题,都被人工智能技术一一化解,可以说人工智能推动了整个计算机视觉领域的发展,是一种非常强大的技术手段。 4 | 5 | 6 | 7 | 这么强大的技术,我们学习它究竟有什么意义呢? 8 | 9 | 1. 结合我自身的切身感受来说,在本科阶段,我自己也参加了很多的学生实验室和科创比赛,越来越多的比赛会涉及到计算机视觉的技术。对于综合类比赛,例如**挑战杯**、**创青春**、**互联网+**等,很多的参赛队伍都使用到了人工智能,作为一大创新点,在比赛中脱颖而出;对于专业类比赛,例如**软件杯**、**robcup**,甚至是今年的**电子设计竞赛**等等,也涉及了很多计算机视觉方面的要求,比如说识别一些特征物体,识别一些数字,或者垃圾分类,这些硬性的题目要求,如果对于计算机视觉不是很了解,那做起来就会非常困难。 10 | 2. 对于发表论文的研究生学长学姐,可以看到现在很多的**交叉学科**,都在将**计算机视觉和相关专业进行融合**,借助视觉这种新的技术手段,对专业内的一些问题提出新的解决方法和思路,举个简单的例子,就我们电气专业来说,可以使用计算机视觉的方法监视绝缘子老化的情况,以往呢可能需要使用一些传感器或者是其他的手段。 11 | 12 | 13 | 14 | ## 开发平台 15 | 16 | 目前,人工智能的开发平台有很多,国外的平台有**torch**、**tensorflow**、**keras**等,国内平台有百度的**paddle**等,每个开发平台都有各自的特点。本期教程是以百度的**paddle**平台为例,版本为2.0以上的动态图版本,和**pytorch**的用法是非常相似的,学完本期的教程,也基本学完了**pytorch**的用法。 17 | 18 | 19 | 20 | 现在已经有了很多非常优秀的深度学习教程,本期教程是以**实际应用**为主,对于原理部分也会做一些简单的介绍,更多地是为了**解决问题,实现相应的功能**,能够让同学们快速地**入门深度学习**,也是我自己对本科阶段学习计算机视觉的回顾和总结。 21 | 22 | 教程里不包括**python**基础语法的讲解,也不包括**numpy**,**opencv**这些基础视觉工具库的讲解,需要同学们自己预先的学习,希望这门教程对大家有所帮助和启发。 23 | 24 | -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/SE.py: -------------------------------------------------------------------------------- 1 | from paddle.nn import Linear,AdaptiveAvgPool2D 2 | import paddle 3 | import paddle.nn as nn 4 | import paddle.nn.functional as F 5 | 6 | class SELayer(nn.Layer): 7 | def __init__(self, num_channels, num_filters, reduction_ratio): 8 | super(SELayer, self).__init__() 9 | self.pool2d_gap = AdaptiveAvgPool2D(1) 10 | self._num_channels = num_channels 11 | self.squeeze = Linear(num_channels,int(num_channels / reduction_ratio)) 12 | self.excitation = Linear(int(num_channels / reduction_ratio),num_filters) 13 | 14 | def forward(self, input): 15 | pool = self.pool2d_gap(input) #[1,256,1,1] 16 | pool = paddle.squeeze(pool, axis=[2, 3]) #[1,256] 17 | squeeze = self.squeeze(pool) #[1,16] 18 | squeeze = F.relu(squeeze) #[1,16] 19 | excitation = self.excitation(squeeze) #[1,256] 20 | excitation = F.sigmoid(excitation) #[1,256] 21 | excitation = paddle.unsqueeze(excitation, axis=[2, 3]) #[1,256,1,1] 22 | out = input * excitation #[1,256,64,64] 23 | return out 24 | 25 | # Basic = SELayer(num_channels=256,num_filters=256,reduction_ratio=16) 26 | # params_info = paddle.summary(Basic,(1,256,64,64)) 27 | # print(params_info) -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/model.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import numpy as np 3 | from paddle.nn import Conv2D, MaxPool2D, Linear 4 | import paddle.nn.functional as F 5 | 6 | # 定义 LeNet 网络结构 7 | class LeNet(paddle.nn.Layer): 8 | def __init__(self, num_classes=1): 9 | super(LeNet, self).__init__() 10 | self.conv1 = Conv2D(in_channels=1, out_channels=6, kernel_size=5) 11 | self.max_pool1 = MaxPool2D(kernel_size=2, stride=2) 12 | self.conv2 = Conv2D(in_channels=6, out_channels=16, kernel_size=5) 13 | self.max_pool2 = MaxPool2D(kernel_size=2, stride=2) 14 | self.conv3 = Conv2D(in_channels=16, out_channels=120, kernel_size=4) 15 | self.fc1 = Linear(in_features=120, out_features=64) 16 | self.fc2 = Linear(in_features=64, out_features=num_classes) 17 | def forward(self, x): #[10,1,28,28] 18 | x = self.conv1(x) #[10,6,24,24] 19 | x = F.sigmoid(x) #[10,6,24,24] 20 | x = self.max_pool1(x) #[10,6,12,12] 21 | x = F.sigmoid(x) #[10,6,12,12] 22 | x = self.conv2(x) #[10,16,8,8] 23 | x = self.max_pool2(x) #[10,16,4,4] 24 | x = self.conv3(x) #[10,120,1,1] 25 | x = paddle.reshape(x, [x.shape[0], -1]) #[10,120] 26 | x = self.fc1(x) #[10,64] 27 | x = F.sigmoid(x) #[10,64] 28 | x = self.fc2(x) #[10,10] 29 | return x -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/rubbish/cls.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | def ReadFileDatas(): 5 | FileNamelist = [] 6 | file = open('train.txt','r+') 7 | for line in file: 8 | line=line.strip('\n') #删除每一行的\n 9 | FileNamelist.append(line) 10 | #print('len ( FileNamelist ) = ' ,len(FileNamelist)) 11 | file.close() 12 | return FileNamelist 13 | 14 | def WriteDatasToFile(listInfo): 15 | file_handle_train=open('train.txt',mode='w') 16 | file_handle_eval = open("eval.txt",mode='w') 17 | i = 0 18 | for idx in range(len(listInfo)): 19 | str = listInfo[idx] 20 | #查找最后一个 “_”的位置 21 | ndex = str.rfind('_') 22 | #print('ndex = ',ndex) 23 | #截取字符串 24 | str_houZhui = str[(ndex+1):] 25 | #print('str_houZhui = ',str_houZhui) 26 | str_Result = str + '\n' #+ str_houZhui+'\n' 27 | #print(str_Result) 28 | if(i%6 != 0): 29 | file_handle_train.write(str_Result) 30 | else: 31 | file_handle_eval.write(str_Result) 32 | i += 1 33 | file_handle_train.close() 34 | file_handle_eval.close() 35 | 36 | path = './' 37 | res = os.listdir(path) 38 | print(res) 39 | with open("train.txt","w") as f: 40 | for i in res: 41 | if(os.path.isdir(i)): 42 | path1 = path + i 43 | res2 = os.listdir(path1) 44 | for j in res2: 45 | f.write(path1+"/"+j+" " + i +'\n') 46 | 47 | listFileInfo = ReadFileDatas() 48 | #打乱列表中的顺序 49 | random.shuffle(listFileInfo) 50 | WriteDatasToFile(listFileInfo) -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/Paddledetection_tutorial/configs/yolov3/_base_/yolov3_reader.yml: -------------------------------------------------------------------------------- 1 | worker_num: 2 2 | TrainReader: 3 | inputs_def: 4 | num_max_boxes: 50 5 | sample_transforms: 6 | - Decode: {} 7 | - Mixup: {alpha: 1.5, beta: 1.5} 8 | - RandomDistort: {} 9 | - RandomExpand: {fill_value: [123.675, 116.28, 103.53]} 10 | - RandomCrop: {} 11 | - RandomFlip: {} 12 | batch_transforms: 13 | - BatchRandomResize: {target_size: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608], random_size: True, random_interp: True, keep_ratio: False} 14 | - NormalizeBox: {} 15 | - PadBox: {num_max_boxes: 50} 16 | - BboxXYXY2XYWH: {} 17 | - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True} 18 | - Permute: {} 19 | - Gt2YoloTarget: {anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]], anchors: [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]], downsample_ratios: [32, 16, 8]} 20 | batch_size: 1 21 | shuffle: true 22 | drop_last: true 23 | mixup_epoch: 250 24 | use_shared_memory: true 25 | 26 | EvalReader: 27 | inputs_def: 28 | num_max_boxes: 50 29 | sample_transforms: 30 | - Decode: {} 31 | - Resize: {target_size: [608, 608], keep_ratio: False, interp: 2} 32 | - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True} 33 | - Permute: {} 34 | batch_size: 1 35 | 36 | TestReader: 37 | inputs_def: 38 | image_shape: [3, 608, 608] 39 | sample_transforms: 40 | - Decode: {} 41 | - Resize: {target_size: [608, 608], keep_ratio: False, interp: 2} 42 | - NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True} 43 | - Permute: {} 44 | batch_size: 1 45 | -------------------------------------------------------------------------------- /第02讲 计算机视觉的分类/README.md: -------------------------------------------------------------------------------- 1 | ## 传统计算机视觉方法 2 | 3 |
4 | 5 |
6 | 7 | 传统的计算机视觉可以使用**Opencv**等Python库,对图像进行简单的操作,例如对图像**缩放**、**滤波**、**阈值分割**等等。对于计算机来说,一张彩色图片就是一个**三通道的矩阵**,分别对应红绿蓝(RGB)三种颜色,通过改变颜色的数值(**0-255**)来显示出一张完整的彩色图片,传统的计算机视觉就是围绕这一个三维矩阵,比如设置一个颜色区间,进行过滤等等操作。 8 | 9 | 这一类视觉处理的方法,功能相对较弱一些,能够处理一些简单的应用场景,比如**识别绿色物体**,**识别动态的物体**等。但是对于**背景复杂的实际场景中,很多问题都难以解决**。 10 | 11 | 推荐Opencv教程地址:https://github.com/CodecWang/opencv-python-tutorial 12 | 13 | ## 深度学习 14 | 15 | 通过人工智能对图像进行处理的算法有很多,其中最为经典的为**卷积神经网络**,对原始图像不停**卷积运算**,**充分提取特征**,最后输出想要的结果,这类方法经过实践的验证取得了**非常不错的精度表现**,在目前的很多硬件上,都能够跑出**实时**的效果。 16 | 17 |
18 | 19 |
20 | 21 | 当然,更多新型的视觉处理算法也涌现出来,比如最近比较火热的**Transformer**算法,最初应用于**NLP**(**自然语言处理**),最近科研者们发现它在视觉领域也展现出了非常不错的表现,很多领域下都取得了最佳的精度,**突破了卷积神经网络的精度瓶颈**。我们这期教程还是围绕卷积神经网络,这种经典的算法展开,仍然值得大家深入地学习。 22 | 23 |
24 | 25 |
26 | 27 | ## 计算机视觉任务的分类 28 | 29 | ### 分类(Classification) 30 | 31 | 分类任务是对整张图片进行分类,例如最为经典的**猫狗分类**。 32 | 33 |
34 | 35 |
36 | 37 | 猫狗分类就是让计算机对于我指定的图片进行归类,如果这张图片是猫,我把图片输入到模型后,我期望输出的就是猫这个类别。可以看到,分类任务是对**整张图片的归类**,如果一张图片里面既有猫,又有狗,那么显然分类无法完成,因为分类任务是**不需要对物体定位**的。分类任务是计算机视觉最简单的任务,实现的**难度最低**,当然**功能也最为简单**。 38 | 39 | ### 检测(Detection) 40 | 41 |
42 | 43 |
44 | 45 | 检测任务相对于分类任务,需要精确地对图像中的**目标物体定位**,一般用**矩形框确定目标位置**。如上图,一张图片中,有狗,有自行车,有汽车,对于检测任务,就需要精确地**框出他们的位置**,**并判别类别**。检测任务是对图像中的物体进行特征识别,相比分类任务难度有所提升,也是我们经常会有的需求,需要精确判定特征物体在画面中的位置,例如**行人检测**,**人脸检测**等等。 46 | 47 | ### 分割(Segmentation) 48 | 49 |
50 | 51 |
52 | 53 | 54 | 分割任务的难度再次增加,任务要求不仅需要确定位置,还需要**勾勒出物体的轮廓**,类似**PS的抠图**,过滤去背景。例如上图所示的**工业读表**,**车道线分割**等等,这类任务对于模型和算法的考验较大,在特定的场合中有一定的应用。 55 | 56 | -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/train.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # LeNet 识别手写数字 3 | import imp 4 | import paddle 5 | import numpy as np 6 | import paddle 7 | from model import LeNet 8 | from paddle.vision.transforms import ToTensor 9 | from paddle.vision.datasets import MNIST 10 | 11 | 12 | def train(model, opt, train_loader, valid_loader): 13 | use_gpu = True 14 | paddle.device.set_device('gpu:0') if use_gpu else paddle.device.set_device('cpu') 15 | print('start training ... ') 16 | model.train() 17 | for epoch in range(EPOCH_NUM): 18 | for batch_id, data in enumerate(train_loader()): 19 | img = data[0] #[10,1,28,28] 20 | label = data[1] #[10,1] 21 | # 计算模型输出 22 | logits = model(img) 23 | # 计算损失函数 24 | loss_func = paddle.nn.CrossEntropyLoss(reduction='none') 25 | loss = loss_func(logits, label) 26 | avg_loss = paddle.mean(loss) 27 | 28 | if batch_id % 500 == 0: 29 | print("epoch: {}, batch_id: {}, loss is: {:.4f}".format(epoch+1, batch_id, float(avg_loss.numpy()))) 30 | avg_loss.backward() 31 | opt.step() 32 | opt.clear_grad() 33 | 34 | model.eval() 35 | accuracies = [] 36 | losses = [] 37 | for batch_id, data in enumerate(valid_loader()): 38 | img = data[0] 39 | label = data[1] 40 | # 计算模型输出 41 | logits = model(img) 42 | # 计算损失函数 43 | loss_func = paddle.nn.CrossEntropyLoss(reduction='none') 44 | loss = loss_func(logits, label) 45 | acc = paddle.metric.accuracy(logits, label) 46 | accuracies.append(acc.numpy()) 47 | losses.append(loss.numpy()) 48 | print("[validation] accuracy/loss: {:.4f}/{:.4f}".format(np.mean(accuracies), np.mean(losses))) 49 | model.train() 50 | 51 | # 保存模型参数 52 | paddle.save(model.state_dict(), 'mnist.pdparams') 53 | 54 | 55 | model = LeNet(num_classes=10) 56 | EPOCH_NUM = 5 57 | opt = paddle.optimizer.Momentum(learning_rate=0.001, parameters=model.parameters()) 58 | train_loader = paddle.io.DataLoader(MNIST(mode='train', transform=ToTensor()), batch_size=10, shuffle=True) 59 | valid_loader = paddle.io.DataLoader(MNIST(mode='test', transform=ToTensor()), batch_size=10) 60 | train(model, opt, train_loader, valid_loader) -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/README.md: -------------------------------------------------------------------------------- 1 | ## 残差结构 2 | 3 | 残差网络在2015年,由**何凯明**等四位中国深度学习研究者在论文《**Deep Residual Learning for Image Recognition**》提出,极大地提高了卷积神经网络在**图像领域的精度**。残差网络中反复使用到了**残差结构**,这种结构在之后的**新型网络中被反复使用**。 4 | 5 | 为什么会提出这种结构?原因在于,更加**深层的卷积网络**往往更能提取出图像的特征,而且拟合能力更强,这样的深层网络给训练带来了很大的困难。**网络层数越深,SGD优化梯度变得更困难**,很容易出现**梯度为0或者梯度爆炸**的情况。残差网络很好地解决了这个问题。 6 | 7 |
8 | 9 |
10 | 11 | 残差结构的通用形式如上图,输入**X分成两路**,一路不经过任何的操作(**shortcut**),另外一路经过相应的权重层和激活函数(**residual**),最后将这两路**直接相加**,再经过一个激活函数输出结果。 12 | 13 | 可以看到残差结构有一个明显的特征就是有**一条捷径什么都不做,直接和另一路相加**。相当于将原始图像和处理后图像进行了**特征融合**,而且**解决了难以训练的问题**。这个简单的加法并不会给网络增加**额外的参数和计算量**,却可以大大增加模型的训练速度、提高训练效果。 14 | 15 | 作者提出了两种具体的实现方式: 16 | 17 |
18 | 19 |
20 | 21 | 左图对应输入通道为64时,用两个(3,3)的卷积,中间不改变通道数,最后**相加激活**得到输出。右图对应于输入通道为256时,先用一个(1,1)的卷积把**通道数缩小为1/4**,然后在这个通道数上进行(3,3)的卷积,最后再经过一个(1,1)的卷积改为原通道,然后**相加激活**的到输出。 22 | 23 | **ResNet34中使用的是左图的残差结构,ResNet50/101/152使用的是右图的残差结构。** 24 | 25 | ## **深度可分离卷积** 26 | 27 | 深度可分离卷积也是一个非常常用的卷积结构,特别是在**轻量化的卷积网络**中应用广泛。它的结构如下图: 28 | 29 |
30 | 31 |
32 | 33 | 对于一个输入矩阵,我先用一个(3,3)的卷积,各个通道**不相加**,这个过程其实就是**分组卷积**,**分组的个数等于输入通道数**,之后再用(1,1)的卷积改变**输出通道数**。 34 | 35 | 可以明显地看出,深度可分离卷积**极大地降低了卷积的参数量**,这一大有点也就意味着这种卷积结构**非常适合应用于轻量化的网络**中。例如**Google团队**开发的**MobileNet**网络。其中就用到了很多这样的结构。 36 | 37 |
38 | 39 |
40 | 41 | **MobileNetV2**中先用(1,1)的卷积改变通道数,对于这个输出用**深度可分离卷积**,最后也使用了**残差结构**,有一个相加的部分。和原始残差结构不同的是,原始残差结构**中间通道数最小,两头大**,**MobileNet**中**中间通道数大,两头小**。这种结构也叫做**反残差**。 42 | 43 | ## 注意力机制 44 | 45 | **注意力机制**是当前研究的热点问题,这里我们介绍最简单的**SE注意力机制**。SE注意力机制把目光放在了**通道之间的关系上**,之前所有的所有运算,**对于所有通道数都是一视同仁的**。**SE注意力机制**希望模型可以**自动学习到不同channel特征的重要程度**,因为很显然各个通道间**所包含特征的重要程度是不一样的**。它的结构如下: 46 | 47 |
48 | 49 |
50 | 51 | 通过一个**全局平局池化层**,将**输入大小压缩成1**,通道数不变,我们对这个1维的矩阵做**激励**,这样得到一个一维的矩阵,相当于是**各个通道的权重**,将这个激励和原输入**相乘**,就得到了一个**通道重要程度不同的输出**。这样就区分出了各个通道的重要性。 52 | 53 | 原始SE注意力机制的**压缩**和**激励**如下: 54 | 55 |
56 | 57 |
58 | 59 | 激励部分使用**两个全连接层**,使用的激活函数为**rule**和**sigmoid**,之后和输入**相乘**即可。 60 | 61 | **ResNet**和**MobileNet**使用**注意力机制**之后,精度都得到了相应的提升。 62 | 63 |
64 | 65 |
66 | 67 | 68 | **ResNet**中的**SE注意力机制**放在了(1,1)卷积之后,激励部分使用的是**全连接层**。MobileNetV3中**SE注意力机制**放在了(1,1)卷积之前,并且激励部分使用的是**卷积层**。 69 | 70 | ## 参考资料 71 | 72 | https://arxiv.org/abs/1512.03385 73 | 74 | https://arxiv.org/abs/1905.02244 75 | 76 | https://arxiv.org/abs/1709.01507 77 | 78 | https://blog.csdn.net/qq_42617455/article/details/108165206 79 | 80 | https://machinethink.net/blog/mobilenet-v2/ 81 | 82 | https://mp.weixin.qq.com/s?__biz=MzA5ODEzMjIyMA==&mid=2247496109&idx=1&sn=a7a6558b086266c062925600e3e71394&source=41#wechat_redirect 83 | -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/README.md: -------------------------------------------------------------------------------- 1 | ## YOLOv3整体结构 2 | 3 |
4 | 5 |
6 | 7 | ## Neck 8 | 9 | ### 整体结构 10 | 11 | **YOLOv3**的**neck**部分使用的是**FPN**,这一部分也叫**特征金字塔**,它的作用是将**多尺度的出入进行特征融合**。 12 | 13 | **backbone**部分输出的shape分别为(13,13,1024),(26,26,512),(52,52,256)。将这三个输出分别输入到FPN中,我们先看(13,13,1024)这一个输入,**经过5次卷积后**,输出(13,13,512),然后**兵分两路**,一路传入到**head**中,一路再经过**一个卷积和上采样**,得到(26,26,256),将这个输出和**backbone**的第2个输出也就是(26,26,512)**堆叠**(**concat**),得到(26,26,768)。 14 | 15 | 卷积池化使得图像**大小越来越小**,这个过程叫**下采样**。**上采样就是通过插值的方法,扩充图像大小**,就像这边的(13,13)变成(26,26)。 16 | 17 | **concat**操作是把两个矩阵**堆叠**到一起,里面的数不变,只是单纯的堆叠,**shape等于两个相加**。这一点希望大家和残差结构中的**add**区分开,相加操作需要**两个输入shape一致,里面的数也会相加**。 18 | 19 | 后续部分其实就是第一种操作的重复。堆叠后的矩阵,经过5次卷积,再兵分两路,一路输入head,一路经过卷积上采样,和backbone第一个输出堆叠。最后通过五次卷积,输入到head中。 20 | 21 | ### 主要特点 22 | 23 |
24 | 25 |
26 | 27 | **FPN有3个输入,3个输出**,输入shape分别为(13,13,1024),(26,26,512),(52,52,256),输出shape分别为(13,13,512),(26,26,256),(52,52,128)。 28 | 29 | 很清晰地可以看出,**FPN只有深层向浅层的融合,并没有浅层向深层的融合**。后续诞生的**PAN**结构,除了有深层向浅层的融合,还有浅层向深层的融合,是FPN的一个升级版,有兴趣的同学可以自己学习。 30 | 31 | ## Head 32 | 33 | **Head**部分非常简单,包含一个**3×3的卷积层和1×1的卷积层**。3×3的卷积层将**通道数扩充一倍**,1×1的卷积层做最后的调整,也就是把通道数缩小到3×(num_class+4+1)。 34 | 35 | 这样通过**backbone**,**neck**,**head**三部分,完成了从原始图像(416,416,3)到(13,13,75),(26,26,75),(52,52,75)的转化。 36 | 37 | ## 解码过程 38 | 39 | 获得到网络输出的三个矩阵后,并不是**直接获得了我们最后的预测框**。我们之前说过对于voc数据集,75=3×(20+4+1),其中20和1都是和分类相关的,我们这边**重点看一下这个4**,也就是**对先验框的4个调整的参数**,通过调整后也就输出了最后的预测框。 40 | 41 | 先验框是**固定不变的**,每个特征图,每个图的**每个格子有3个先验框**,所以我们需要预先准备**9个大小的先验框**。 42 | 43 | anchors大小:[116,90],[156,198],[373,326],[30,61],[62,45],[59,119],[10,13],[16,30],[33,23],这些大小是**相对于416×416的尺寸**,我们最后三个输出的大小为13×13,26×26,52×52,所以要进行**相应的缩放**。 44 | 45 |
46 | 47 |
48 | 49 | 我们以13×13的输出为例,原本416×416大小变成13×13,相当于缩小了**32倍**,也就是说**原图32×32个小方块对应于最后输出的1×1的像素点**。anchors[116,90],[156,198],[373,326]相应地长宽都应该**除以32**,这就是**13×13每个点上的三个先验框**。 50 | 51 |
52 | 53 |
54 | 55 | **粉红色**的就是对应到13×13上的**先验框**,它是我们一开始自己就确定的,显然是**不正确**,需要模型对它调整。 56 | 57 | 先验框怎么摆放的呢,它的中心就是落在**13×13的交点**上,长宽就是除以32的结果。先验框坐标记为(cx,cy,pw,ph),模型输出的4为(tx,ty,tw,th),调整的公式如上图所示,中心点取**sigmoid激活函数**,**sigmoid函数范围是0-1**,也就是中心点的调整范围永远在**右下角的框内**,这也就是我们说的,**物体的中心落在哪个格子里,就有哪个框负责预测**。 58 | 59 | **长宽取exp后相乘**。这就得到了在13×13尺寸图上的预测框,然后**再乘以32缩放回来就得到了最后的预测框**。 60 | 61 | ## YOLOv3的改进方法 62 | 63 | YOLOv3仍有很多改进的**tricks**,如更换新型的**backbone**,原始的darknet53参数量较大,对于一些终端设备难以适配;增加一些**注意力机制**;使用**新型的数据增强方法**等。当然也可以**调整输出**,或者是**调整每个格子上先验框的个数**。 64 | 65 | ## 参考资料 66 | 67 | https://blog.csdn.net/weixin_44791964/article/details/105310627 68 | 69 | https://blog.csdn.net/Sagacity_1125/article/details/120550505?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~aggregatepage~first_rank_ecpm_v1~rank_v31_ecpm-1-120550505.pc_agg_new_rank&utm_term=yolov3+%E8%A7%A3%E7%A0%81&spm=1000.2123.3001.4430 70 | -------------------------------------------------------------------------------- /第12讲 实战:PaddleDeteciton实现目标检测/README.md: -------------------------------------------------------------------------------- 1 | ## 下载链接 2 | 3 | 链接:https://pan.baidu.com/s/1ydUkMBOUQtaIaNJrjbCjGQ 4 | 提取码:qxp9 5 | 6 | ## 数据集 7 | 8 | ### 数据集结构 9 | 10 | 在实际项目中最常用的是voc数据集格式,我们以voc数据集为例,进行介绍。 11 | 12 |
13 | 14 |
15 | 16 | JPEGImages存放所有数据集图片,Annotations中存放所有标注文件。 17 | 18 | 如图片000001.jpg,它对应的标注就是000001.xml 19 | 20 |
21 | 22 |
23 | 24 | 这张图片对应的标注信息如下: 25 | 26 | ``` 27 | 28 | VOC2007 29 | 000001.jpg 30 | 31 | The VOC2007 Database 32 | PASCAL VOC2007 33 | flickr 34 | 341012865 35 | 36 | 37 | Fried Camels 38 | Jinky the Fruit Bat 39 | 40 | 41 | 353 42 | 500 43 | 3 44 | 45 | 0 46 | 47 | dog 48 | Left 49 | 1 50 | 0 51 | 52 | 48 53 | 240 54 | 195 55 | 371 56 | 57 | 58 | 59 | person 60 | Left 61 | 1 62 | 0 63 | 64 | 8 65 | 12 66 | 352 67 | 498 68 | 69 | 70 | 71 | ``` 72 | 73 | ### 制作数据集 74 | 75 | 使用labelImg软件制作目标检测数据集。 76 | 77 |
78 | 79 |
80 | 81 | 键盘A和D代表上一张和下一张,W为标注工具,标注完成后CTRL+S保存,这样一张图片就标注完成了。所有的图片都需要进行标注,而且不能遗漏目标物体,这一过程是非常耗时耗力的。 82 | 83 |
84 | 85 |
86 | 87 | ## PaddleDetection 88 | 89 | 把制作好的数据集放在dataset文件夹下,然后修改configs里面的标注文件,对应你想要修改的模型,特别需要注意修改dataset相关的参数,如果对模型不做特别修改,其他大多数参数都不需要改变。 90 | 91 | ### 模型训练 92 | 93 | ``` 94 | python tools/train.py -c configs/yolov3/yolov3_darknet53_270e_voc.yml --use_vdl=True --eval 95 | ``` 96 | 97 | ### 断点训练 98 | 99 | ``` 100 | python tools/train.py -c configs/yolov3/yolov3_darknet53_270e_voc.yml -r output/yolov3_darknet53_270e_voc/100 101 | ``` 102 | 103 | ### 模型评估 104 | 105 | ``` 106 | python tools/eval.py -c configs/yolov3/yolov3_darknet53_270e_voc.yml -o weights=output/yolov3_darknet53_270e_voc/best_model 107 | ``` 108 | 109 | ### 模型导出 110 | 111 | ``` 112 | python tools/export_model.py -c configs/yolov3/yolov3_darknet53_270e_voc.yml --output_dir=./inference_model -o weights=output/yolov3_darknet53_270e_voc/best_model 113 | ``` 114 | 115 | ### 模型预测 116 | 117 | ``` 118 | python deploy/python/infer.py --model_dir=./inference_model/yolov3_darknet53_270e_voc --image_file=./street.jpg --device=GPU --threshold=0.2 119 | ``` 120 | 121 | -------------------------------------------------------------------------------- /第04讲 深度学习的基本实现流程/README.md: -------------------------------------------------------------------------------- 1 | ## 深度学习的基础实现流程 2 | 3 | ### 数据集准备 4 | 5 | 深度学习需要**大量数据的支撑**,搭建好的模型,通过大量的数据学习之后,才会拥有**强大的泛化能力**。模型对数据集的所有图片不停地学习,收敛到一定程度之后,输入一张**全新的图片**(不在数据集内),也会输出一个**相对正确的结果**。数据集包含的**场景越多**,**背景越复杂**,最终实现的效果会越好。数据集应该包含项目可能出现的所有情况,如**光照**、**贴纸**等不利干扰情况,都需要在数据集中出现。 6 | 7 | 但是对于我们单个具体项目而言,**拍摄多个场景的物体是比较困难**的,自己架设好摄像头,拍摄出来的背景也是**比较单一**的,难以**满足丰富、复杂、随机的**要求。 8 | 9 |
10 | 11 |
12 | 13 | 如上图自己制作的有害垃圾数据集,因为假设好了机械结构,背景非常单一。电池的种类也非常有限,无法将所有种类的电池都采购到。**拍摄过多类似的图片,不仅无法满足数据集的要求,而且最终的模型精度也会受到较大影响**。所以建议添加一些**网上的现有数据集,或者通过爬虫等方式**,来**扩充数据集的复杂和随机程度**,自己拍摄也可以故意制造一些特有场景。 14 | 15 |
16 | 17 |
18 | 19 | 以目标检测为例,理想情况下,包含单个物体的图片数要**大于1500张**,单个物体的出现次数要**大于10000个**,并且都需要进行**标注**。数据集的制作是**非常耗时**的,尤其在标注时,需要消耗大量的精力。 20 | 21 | ### 数据增强 22 | 23 | 除了尽可能增加图片的个数和复杂度意外,数据增强是最为常用的**扩充数据集**方法,也对模型精度的提升有极大的帮助。可以说数据增强是一个非常有实用的技巧。 24 | 25 |
26 | 27 |
28 | 29 | 可以看到,相比于原图,可以**随机地改变对比度、亮度,或是扣去一些像素点**,甚至是**多张图片的重叠、拼接**,这些都是数据增强的手段,数据增强可以提高难度,也可以成倍地扩充数据集,这对于**大多数场景来说都是非常有效的**,特别是对于数据集不充足的情况下,使用数据增强可以**极大地提高精度和泛化能力**。然后,需要注意的是,对于**绝大多数轻量化的小模型,应该尽量减少数据增强**,因为小模型的拟合能力有限,过度增加图片复杂程度,不利于最终的收敛过程。 30 | 31 | ### 模型的搭建 32 | 33 | 深度学习的模型是最为关键的一个环节,对于计算机视觉来说,模型一般都是由**卷积结构组成的卷积神经网络**。 34 | 35 |
36 | 37 |
38 | 39 | 上图是**YOLOv3的整体模型结构**,YOLOv3是目标检测的一个经典算法。可以看到整个模型都是由卷积结构组成,对于原始(416,416,3)的原始图像,输入模型之后,输出了三个结果,对这三个结果进行筛选后就可以得到预测框的位置,最终实现以下的效果。 40 | 41 |
42 | 43 |
44 | 45 | 模型内**待求的参数一般都比较多**,不同于我们基础学科,用一些多项式拟合的方法,计算机视觉模型的参数是非常多的,这也就意味着它的**拟合能力非常强**,模型也是非常庞大的,对于一般的图像任务都能够找到**相应的拟合结果**,也拥有**很强的泛化能力**。 46 | 47 | ### 模型的训练 48 | 49 | 显然,原始的模型参数是**无法一下子获得我们想要的结果的**,针对我们特性化的项目要求,比如我要检测易拉罐,我们检测行人等等,需要**训练我们的模型**,让模型知道我们到底要识别什么东西。换句话来说,就是将我们刚刚准备好的数据集,不停地导入到我们的模型里,我们的模型会输出一个**结果(pred)**,也就是**预测值**,将这个预测值和我们标注的**真值(target)求偏差(loss)**,利用数学的方法降低这个偏差,那么就意味着预测值会越来越接近真值,也就是模型输出的结果越来越符合我的要求。 50 | 51 | 举个简单的例子,例如**输入为x,真值为y,模型为F(X)**,预测值pred为: 52 | $$ 53 | pred = F(x) 54 | $$ 55 | 再用最简单公式求出误差: 56 | $$ 57 | loss =(pred - y)^2 = (F(x)-y)^2 58 | $$ 59 | 可以看出loss是和模型所有参数都有关的一个函数,假设**loss的曲线**如下: 60 | 61 |
62 | 63 |
64 | 65 | 我们的目标是让**误差loss尽可能低**,也就是**接近C点附近**,对于A、B、D的情况,我们都希望**修正模型参数x来降低loss**,这就是**优化器**的工作。如果使用**梯度下降法(GD)**,它是这样修正的: 66 | $$ 67 | {x_i} = {x_i} - \alpha \times {\partial \over {\partial {x_i}}}\left( {F\left( x \right)} \right) 68 | $$ 69 | 其中的**a为步长,也称为学习率**,梯度下降法可以使得其他点向C点收敛,但主要存在两个问题。 70 | 71 | 1. 需要求解所有参数的梯度,计算量非常庞大 72 | 2. 容易陷入到局部最优解中,如图中的第一个极小值 73 | 74 | 目前我们常用的优化器有**SGD、Adam**等。**SGD**也就是**随机梯度下降法**,但是虽然包含一定的随机性,从期望上来看,它是等于正确的导数的,每次都只需要对一部分参数求梯度,**大大减少了计算量**。同时由于随机性,可以**有效地躲开局部最优解**,也就是鞍点。解决了GD的两大问题,在计算机视觉领域受到了广泛的使用。 75 | 76 | ### 模型的部署和预测 77 | 78 | 对于绝大多数项目,一般都需要将模型部署到具体的硬件中,比如说**ARM开发板**、**FPGA**等,**部署也是非常重要的一个板块**。 79 | 80 |
81 | 82 |
83 | 84 | 我们的终端设备**几乎不可能配一个台式主机**,也就意味着没有很强大的计算能力和存储能力。部署的关键在于保证精度的情况下,**尽可能压缩模型体积,提升模型预测速度**。可以使用现在一些开源的工具,如**ncnn**、**tensorrt**、**paddle-lite**等进行相应的部署。 85 | 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 基于Paddle的计算机视觉入门教程 2 | 3 | - CSDN博客地址:https://blog.csdn.net/weixin_45747759?spm=1010.2135.3001.5343 4 | - Github仓库地址:https://github.com/Monday-Leo/Paddle_tutorial 5 | - B站视频教程地址:https://www.bilibili.com/video/BV18b4y1J7a6/ 6 | 7 | ## 简介 8 | 9 | 这是一个基于Paddle2.0版本的计算机视觉教程,本次教程包含了众多计算机视觉的基础知识。从基础环境搭建开始,讲解了深度学习的基本流程,基础的卷积、池化等运算操作,分析了分类任务和检测任务的具体实现流程和算法。同时动手实战了垃圾分类、手写数字识别、目标检测三个具体项目。 10 | 11 | ## 目录 12 | 13 | | 标题 | 简介 | 14 | | ------------------------------------------------------------ | ----------------------------------------------------------- | 15 | | [介绍](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC01%E8%AE%B2%20%E4%BB%8B%E7%BB%8D) | 计算机视觉和本期教程介绍 | 16 | | [计算机视觉的分类](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC02%E8%AE%B2%20%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89%E7%9A%84%E5%88%86%E7%B1%BB) | 传统计算机视觉和深度学习 | 17 | | [环境搭建](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC03%E8%AE%B2%20%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA) | Anaconda、VS Code、CUDA和Paddle的安装 | 18 | | [深度学习的基本实现流程](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC04%E8%AE%B2%20%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%AE%9E%E7%8E%B0%E6%B5%81%E7%A8%8B) | 深度学习基本的五大步骤 | 19 | | [PaddleX实现垃圾分类](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC05%E8%AE%B2%20%E5%AE%9E%E6%88%98%EF%BC%9APaddleX%E5%AE%9E%E7%8E%B0%E5%9E%83%E5%9C%BE%E5%88%86%E7%B1%BB) | 实战:利用PaddleX入门简单的图像分类 | 20 | | [常用的运算操作](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC06%E8%AE%B2%20%E5%B8%B8%E7%94%A8%E7%9A%84%E8%BF%90%E7%AE%97%E6%93%8D%E4%BD%9C) | 卷积、池化、归一化和激活函数的讲解 | 21 | | [手写数字识别](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC07%E8%AE%B2%20%E5%AE%9E%E6%88%98%EF%BC%9A%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB) | 实战:从零开始实现手写数字识别全流程 | 22 | | [常用的卷积结构](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC08%E8%AE%B2%20%E5%B8%B8%E7%94%A8%E7%9A%84%E5%8D%B7%E7%A7%AF%E7%BB%93%E6%9E%84) | 残差结构,深度可分离卷积以及SE注意力机制详解 | 23 | | [MobilenetV3网络详解](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC09%E8%AE%B2%20MobilenetV3%E7%BD%91%E7%BB%9C%E8%AF%A6%E8%A7%A3) | MobilenetV3的主体结构以及使用PaddleClas完成训练垃圾分类任务 | 24 | | [目标检测-YOLOv3](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC10%E8%AE%B2%20%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8BYOLOv3-backbone) | YOLOv3-backbone部分介绍 | 25 | | [目标检测-YOLOv3](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC11%E8%AE%B2%20%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8BYOLOv3-neck%E3%80%81head) | YOLOv3-neck、head和解码部分介绍 | 26 | | [目标检测-YOLOv3](https://github.com/Monday-Leo/Paddle_tutorial/tree/master/%E7%AC%AC12%E8%AE%B2%20%E5%AE%9E%E6%88%98%EF%BC%9APaddleDeteciton%E5%AE%9E%E7%8E%B0%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B) | 实战:PaddleDeteciton实现目标检测 | 27 | 28 | -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/PaddleClas-release-2.3/MobileNetV3_large_x1_0.yaml: -------------------------------------------------------------------------------- 1 | # global configs 2 | Global: 3 | checkpoints: null 4 | pretrained_model: null 5 | output_dir: ./output/ 6 | device: gpu 7 | save_interval: 1 8 | eval_during_train: True 9 | eval_interval: 1 10 | epochs: 20 11 | print_batch_step: 10 12 | use_visualdl: True 13 | # used for static mode and model export 14 | image_shape: [3, 224, 224] 15 | save_inference_dir: ./inference 16 | 17 | # model architecture 18 | Arch: 19 | name: MobileNetV3_large_x1_0 20 | class_num: 4 21 | 22 | # loss function config for traing/eval process 23 | Loss: 24 | Train: 25 | - CELoss: 26 | weight: 1.0 27 | Eval: 28 | - CELoss: 29 | weight: 1.0 30 | 31 | 32 | Optimizer: 33 | name: Momentum 34 | momentum: 0.9 35 | lr: 36 | name: Cosine 37 | learning_rate: 0.00375 38 | warmup_epoch: 5 39 | last_epoch: -1 40 | regularizer: 41 | name: 'L2' 42 | coeff: 0.000001 43 | 44 | 45 | # data loader for train and eval 46 | DataLoader: 47 | Train: 48 | dataset: 49 | name: ImageNetDataset 50 | image_root: ./dataset/rubbish/ 51 | cls_label_path: ./dataset/rubbish/train.txt 52 | transform_ops: 53 | - DecodeImage: 54 | to_rgb: True 55 | channel_first: False 56 | - RandCropImage: 57 | size: 224 58 | - RandFlipImage: 59 | flip_code: 1 60 | - NormalizeImage: 61 | scale: 1.0/255.0 62 | mean: [0.485, 0.456, 0.406] 63 | std: [0.229, 0.224, 0.225] 64 | order: '' 65 | 66 | sampler: 67 | name: DistributedBatchSampler 68 | batch_size: 16 69 | drop_last: False 70 | shuffle: True 71 | loader: 72 | num_workers: 1 73 | use_shared_memory: True 74 | 75 | Eval: 76 | dataset: 77 | name: ImageNetDataset 78 | image_root: ./dataset/rubbish/ 79 | cls_label_path: ./dataset/rubbish/eval.txt 80 | transform_ops: 81 | - DecodeImage: 82 | to_rgb: True 83 | channel_first: False 84 | - ResizeImage: 85 | resize_short: 256 86 | - CropImage: 87 | size: 224 88 | - NormalizeImage: 89 | scale: 1.0/255.0 90 | mean: [0.485, 0.456, 0.406] 91 | std: [0.229, 0.224, 0.225] 92 | order: '' 93 | sampler: 94 | name: DistributedBatchSampler 95 | batch_size: 16 96 | drop_last: False 97 | shuffle: False 98 | loader: 99 | num_workers: 1 100 | use_shared_memory: True 101 | 102 | Infer: 103 | infer_imgs: docs/images/whl/demo.jpg 104 | batch_size: 1 105 | transforms: 106 | - DecodeImage: 107 | to_rgb: True 108 | channel_first: False 109 | - ResizeImage: 110 | resize_short: 256 111 | - CropImage: 112 | size: 224 113 | - NormalizeImage: 114 | scale: 1.0/255.0 115 | mean: [0.485, 0.456, 0.406] 116 | std: [0.229, 0.224, 0.225] 117 | order: '' 118 | - ToCHWImage: 119 | PostProcess: 120 | name: Topk 121 | topk: 4 122 | class_id_map_file: ./dataset/rubbish/labels.txt 123 | 124 | Metric: 125 | Train: 126 | - TopkAcc: 127 | topk: [1, 4] 128 | Eval: 129 | - TopkAcc: 130 | topk: [1, 4] 131 | -------------------------------------------------------------------------------- /第06讲 常用的运算操作/README.md: -------------------------------------------------------------------------------- 1 | ## 卷积(Convolution) 2 | 3 | 卷积是卷积神经网络中最常用的操作,卷积到底是如何运算的呢? 4 | 5 |
6 | 7 |
8 | 9 | 我们先从**一维**来看,如上图所示,有一个**滑动的窗口**从左到右,从上到下滑动,这个滑动的3*3的矩阵我们就称作**卷积核**,卷积核里面的数和输入矩阵的数字**一一相乘后相加**,就得到了一个新的数字,这些新的数字按从左到右、从上到下的顺序排列,就得到了输出矩阵,整个这个过程,我们就叫做**卷积**。 10 | 11 |
12 | 13 |
14 | 15 | 通过一维的卷积我们可以看到,**输入的矩阵内的数字是固定的**,输出矩阵的数字其实完全取决于**卷积核内的数字**,如果我想要输出正确的结果,只能通过**修正卷积核**。换句话来说,**卷积核就是我们需要修正的参数**,这一点希望大家能够明确。 16 | 17 | 对于图像来讲,我们看到的都是**三通道的彩色图**(RGB格式),也就是说**最初的输入是一个3通道的矩阵**,比如(3,640,640),对于多通道的矩阵,卷积操作也不难理解。 18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |
26 | 27 | 其实就是将**一维的卷积重复了三次**,**有3个卷积窗口分别滑动得到了三个矩阵,最后将这三个矩阵内的数相加**,得到了最后的输出。可以看到上图的输入shape为(3,5,5),卷积核的shape为(3,3,3),输出的shape为(1,3,3) 28 | 29 | 然而,输入图像往往需要经过多个卷积层才能输出,也就是说,**输入的通道数是不固定的,可能是3、16、64等等**,对于这些情况如何卷积呢? 30 | 31 |
32 | 33 |
34 | 35 | 36 | 如上图所示,有一个通道数很大的输入矩阵,类比我们三通道的卷积方式,其实也不难看出,**卷积核的通道数应该等于输入的通道数**,这样每一层对应起来,其实就变成了一维的形式,最后把每层的结果相加,就得到了一个**一维的输出矩阵**。 37 | 38 | 到这边其实还没有结束,因为除了输入通道以外,我们还希望可以**控制输出通道数**。具体如何控制输出通道数呢? 39 | 40 |
41 | 42 |
43 | 44 | 就是用**Dout个卷积核**重复我们上一步的操作,这样的话,**输入通道和输出通道都可以是任意的数**。 45 | 46 | 我们可以作出以下的总结,对于一个(Din,Hin,Win)的输入矩阵,经过Dout个(Din,h,w)的卷积核之后,输出为(Dout,Hout,Wout)的矩阵。换句话来说**卷积核的通道数等于输入通道数,卷积核的个数等于输出通道数。** 47 | 48 | 49 | 50 | ## 池化(Pooling) 51 | 52 | 除了卷积以外,池化也是一个比较常用的运算操作,池化主要分为两种**最大池化和平均池化。** 53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 | 上图分别为最大池化和平均池化,池化窗口的大小为(2,2),步长为2,**池化就是将这个窗口内的数变成一位数**,对于最大池化来说,就是变成**窗口内的最大数**,平均池化就是变成**窗口内所有数的平均值**。对于多通道的输入来说,池化和卷积不同,池化就是对每一个通道上都进行这样的操作,最后不需要加在一起,也就是说**池化前后的输入输出通道不变**,仅仅改变了特征图的大小。 63 | 64 | 更加直观得来说,池化其实相当于对特征图进行了resize的操作。图像的**特征特征不会明显改变,池化可以有效地减少参数量,而且可以有效地防止过拟合。** 65 | 66 | 67 | 68 | ## 归一化(Normalization) 69 | 70 | 归一化的含义也非常简单,**就是把矩阵内所有的数不失真地映射到0-1之间**。这样做可以**防止某一维或某几维对数据影响过大**,一般我们在卷积运算之后紧跟着都需要进行归一化操作。 71 | 72 |
73 | 74 |
75 | 76 | 左图是未归一化的梯度等高图,右图是归一化之后的图,明显**左图受到某些维度的数据影响过大**,右图梯度下降就**平缓很多**,相对更容易达到**期望的最优解**。 77 | 78 | 79 | 80 | ## 激活函数(Activation) 81 | 82 | 通过以上的讨论,不难发现整个卷积网络都是对矩阵的一些**线性化操作**,对于一些非线性的情况,是**无法精确拟合的**,这就需要增加一些函数略微扭曲输出的结果,**这些函数也被称为激活函数**。 83 | 84 | 常见的激活函数有很多,例如**relu,swish,mish,softmax**等。 85 | 86 |
87 | 88 |
89 | 90 |
91 | 92 |
93 | 94 | 这些激活函数看似**区别不大**,对最终的模型表现却有**极大的影响**。例如有些激活函数是**无上下限的**,有些函数**时间成本很高**,这些都会影响到最后**模型的速度和精度**。 95 | 96 | 97 | 98 | ## 参考资料 99 | 100 | https://mp.weixin.qq.com/s?__biz=MzA5ODEzMjIyMA==&mid=2247496109&idx=1&sn=a7a6558b086266c062925600e3e71394&source=41#wechat_redirect 101 | 102 | https://blog.csdn.net/gwplovekimi/article/details/89890510 103 | 104 | https://blog.csdn.net/program_developer/article/details/78637711?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4.pc_relevant_aa&spm=1001.2101.3001.4242.3&utm_relevant_index=7 105 | -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/Residual.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle.nn import Conv2D, BatchNorm 5 | from SE import SELayer 6 | 7 | class ConvBNLayer(nn.Layer): 8 | def __init__(self, 9 | num_channels, 10 | num_filters, 11 | filter_size, 12 | stride=1, 13 | groups=1, 14 | act=None 15 | ): 16 | super(ConvBNLayer, self).__init__() 17 | self.act = act 18 | self._conv = Conv2D( 19 | in_channels=num_channels, 20 | out_channels=num_filters, 21 | kernel_size=filter_size, 22 | stride=stride, 23 | padding=(filter_size - 1) // 2, 24 | groups=groups) 25 | 26 | self._batch_norm = BatchNorm(num_filters) 27 | 28 | def forward(self, inputs): 29 | y = self._conv(inputs) 30 | y = self._batch_norm(y) 31 | if(self.act=="relu"): 32 | y = F.relu(y) 33 | return y 34 | 35 | 36 | class BasicBlock(nn.Layer): 37 | def __init__(self, 38 | num_channels, 39 | num_filters, 40 | stride): 41 | super(BasicBlock, self).__init__() 42 | self.stride = stride 43 | self.conv0 = ConvBNLayer( 44 | num_channels=num_channels, 45 | num_filters=num_filters, 46 | filter_size=3, 47 | stride=stride, 48 | act="relu") 49 | 50 | self.conv1 = ConvBNLayer( 51 | num_channels=num_filters, 52 | num_filters=num_filters, 53 | filter_size=3, 54 | act=None) 55 | 56 | def forward(self, inputs): 57 | y = self.conv0(inputs) 58 | conv1 = self.conv1(y) 59 | y = paddle.add(x=inputs, y=conv1) 60 | y = F.relu(y) 61 | return y 62 | 63 | class BottleneckBlock(nn.Layer): 64 | def __init__(self, 65 | num_channels, 66 | num_filters, 67 | stride, 68 | use_se = True, 69 | reduction_ratio = 16): 70 | super(BottleneckBlock, self).__init__() 71 | self.use_se = use_se 72 | 73 | self.conv0 = ConvBNLayer( 74 | num_channels=num_channels, 75 | num_filters=num_filters, 76 | filter_size=1, 77 | act="relu") 78 | 79 | self.conv1 = ConvBNLayer( 80 | num_channels=num_filters, 81 | num_filters=num_filters, 82 | filter_size=3, 83 | stride=stride, 84 | act="relu") 85 | 86 | self.conv2 = ConvBNLayer( 87 | num_channels=num_filters, 88 | num_filters=num_filters * 4, 89 | filter_size=1, 90 | act=None) 91 | if use_se: 92 | self.scale = SELayer( 93 | num_channels=num_filters * 4, 94 | num_filters=num_filters * 4, 95 | reduction_ratio=reduction_ratio) 96 | 97 | def forward(self, inputs): 98 | y = self.conv0(inputs) 99 | y = self.conv1(y) 100 | y = self.conv2(y) 101 | if(self.use_se): 102 | y = self.scale(y) 103 | y = paddle.add(inputs, y) 104 | y = F.relu(y) 105 | return y 106 | 107 | # Basic = BasicBlock(num_channels=256,num_filters=256,stride=1) 108 | # params_info = paddle.summary(Basic,(1,256,64,64)) 109 | # print(params_info) 110 | # Basic = BottleneckBlock(num_channels=256,num_filters=64,stride=1,use_se=False) 111 | # params_info = paddle.summary(Basic,(1,256,64,64)) 112 | # print(params_info) 113 | -------------------------------------------------------------------------------- /第03讲 环境搭建/README.md: -------------------------------------------------------------------------------- 1 | ## 安装Anaconda 2 | 3 | 软件下载链接: 4 | 5 | ``` 6 | https://pan.baidu.com/s/1SqEpDgNycHs7ZHbNfZlv7A 7 | 提取码:mclu 8 | ``` 9 | 10 | 双击打开Anaconda软件安装包: 11 | 12 |
13 | 14 |
15 | 16 | 点击Next,下一步 17 | 18 |
19 | 20 |
21 | 22 | 点击I Agree 23 | 24 |
25 | 26 |
27 | 28 | 勾选**All Users** 29 | 30 |
31 | 32 |
33 | 34 | 这里选择安装位置,可以自己新建文件夹,安装到**C盘以外的磁盘**。 35 | 36 |
37 | 38 |
39 | 40 | 这里**建议勾选第一个选项**,将anaconda添加到系统变量中,这种以后可以使用cmd调用conda,更加方便。 41 | 42 | ### conda换清华源 43 | 44 | ``` 45 | conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 46 | conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge 47 | conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ 48 | 49 | # 从channel中安装包时显示channel的url,这样就可以知道包的安装来源了 50 | conda config --set show_channel_urls yes 51 | ``` 52 | 53 | ### conda显示所有源 54 | 55 | ``` 56 | conda config --show channels 57 | ``` 58 | 59 | ### pip换清华源 60 | 61 | ``` 62 | pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 63 | ``` 64 | 65 | ### pip显示所有源 66 | 67 | ``` 68 | pip config list 69 | ``` 70 | 71 | 72 | 73 | ## 安装VS Code 74 | 75 | 双击打开VS Code安装包 76 | 77 |
78 | 79 |
80 | 81 | 勾选我同意,然后点击下一步 82 | 83 |
84 | 85 |
86 | 87 | 这里**勾选第2和第3个选项**,这样可以通过右键快速地打开工程,可以提升效率。 88 | 89 | ### 安装扩展包 90 | 91 |
92 | 93 |
94 | 95 | 点击VS Code左侧最后一个按钮,扩展工具,搜索**Python**,**Pylance**进行安装。当然,如果想切换成中文,可以安装Chinese (Simplified)扩展。 96 | 97 | 98 | 99 | ## 安装CUDA 100 | 101 | CUDA是加速深度学习计算的工具,诞生于NVIDIA公司,是一个显卡的附加驱动。必须使用**NVIDIA的显卡才能安装**,可以打开任务管理器查看自己的硬件设备。这边演示**CUDA10.2版本**的安装。注意:**30系列的显卡必须使用CUDA11.0以上的版本**,其他显卡既可以使用10也可以使用11版本。 102 | 103 | 双击打开安装包 104 | 105 |
106 | 107 |
108 | 109 |
110 | 111 |
112 | 113 |
114 | 115 |
116 | 117 | 点击同意并继续 118 | 119 |
120 | 121 |
122 | 123 | 选择自定义,点击下一步 124 | 125 |
126 | 127 |
128 | 129 |
130 | 131 |
132 | 133 | 这边可以**更改安装路径,可以新建2个文件夹,将第一第二个安装在一个文件夹,第三个安装在另一个文件夹**,这样可以节省C盘空间。 134 | 135 |
136 | 137 |
138 | 139 | CUDA需要配合cudnn才能正常工作,将cudnn的四个文件,复制到cuda的安装路径即可,即第一个文件夹。 140 | 141 | **cudnn和CUDA的版本对应关系如下:** 142 | 143 | - **CUDA 工具包10.1/10.2 配合 cuDNN v7.6.5** 144 | - **CUDA 工具包 11.0 配合 cuDNN v8.0.2** 145 | - **CUDA 工具包 11.1 配合 cuDNN v8.1.1** 146 | - **CUDA 工具包 11.2 配合 cuDNN v8.2.1** 147 | 148 | 可以使用cmd,输入: 149 | 150 | ``` 151 | nvcc -V 152 | ``` 153 | 154 | 查看CUDA是否安装成功 155 | 156 | 157 | 158 | ## 安装Paddle 159 | 160 | 打开Paddle官网,**选择好CUDA版本10.2(根据刚才安装的CUDA版本)**,注意,**如果没有NVIDIA显卡,请选择CPU版本**。 161 | 162 |
163 | 164 |
165 | 166 | 复制出现的命令到anaconda安装即可,**注意一定要切换到自己想要的环境中(如 Paddle_tutorial)** 167 | 168 | ### Paddle安装校验 169 | 170 | 执行以下的Python代码,检查是否成功调用GPU资源 171 | 172 | ``` 173 | import paddle 174 | paddle.utils.run_check() 175 | ``` 176 | 177 | 出现: 178 | 179 | ``` 180 | PaddlePaddle works well on 1 GPU. 181 | PaddlePaddle works well on 1 GPUs. 182 | PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now. 183 | ``` 184 | 185 | **那么恭喜你,环境配置成功啦!** 186 | -------------------------------------------------------------------------------- /第08讲 常用的卷积结构/DSC.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle.nn import Conv2D, BatchNorm,AdaptiveAvgPool2D 5 | 6 | class ConvBNLayer(nn.Layer): 7 | def __init__(self, 8 | in_c, 9 | out_c, 10 | filter_size, 11 | stride, 12 | padding, 13 | num_groups=1, 14 | if_act=True, 15 | act=None): 16 | super(ConvBNLayer, self).__init__() 17 | self.if_act = if_act 18 | self.act = act 19 | 20 | self.conv = Conv2D( 21 | in_channels=in_c, 22 | out_channels=out_c, 23 | kernel_size=filter_size, 24 | stride=stride, 25 | padding=padding, 26 | groups=num_groups) 27 | 28 | self.bn = BatchNorm(num_channels=out_c) 29 | 30 | def forward(self, x): 31 | x = self.conv(x) 32 | x = self.bn(x) 33 | if self.if_act: 34 | if self.act == "relu": 35 | x = F.relu(x) 36 | elif self.act == "hardswish": 37 | x = F.hardswish(x) 38 | else: 39 | print("The activation function is selected incorrectly.") 40 | exit() 41 | return x 42 | 43 | 44 | class ResidualUnit(nn.Layer): 45 | def __init__(self, 46 | in_c, 47 | mid_c, 48 | out_c, 49 | filter_size, 50 | stride, 51 | use_se, 52 | act="hardswish"): 53 | super(ResidualUnit, self).__init__() 54 | self.if_shortcut = stride == 1 and in_c == out_c 55 | self.if_se = use_se 56 | 57 | self.expand_conv = ConvBNLayer( 58 | in_c=in_c, 59 | out_c=mid_c, 60 | filter_size=1, 61 | stride=1, 62 | padding=0, 63 | if_act=True, 64 | act=act) 65 | 66 | self.bottleneck_conv = ConvBNLayer( 67 | in_c=mid_c, 68 | out_c=mid_c, 69 | filter_size=filter_size, 70 | stride=stride, 71 | padding=int((filter_size - 1) // 2), 72 | num_groups=mid_c, 73 | if_act=True, 74 | act=act) 75 | 76 | if self.if_se: 77 | self.mid_se = SEModule(mid_c) 78 | 79 | self.linear_conv = ConvBNLayer( 80 | in_c=mid_c, 81 | out_c=out_c, 82 | filter_size=1, 83 | stride=1, 84 | padding=0, 85 | if_act=False, 86 | act=None) 87 | 88 | def forward(self, inputs): 89 | x = self.expand_conv(inputs) 90 | x = self.bottleneck_conv(x) 91 | if self.if_se: 92 | x = self.mid_se(x) 93 | x = self.linear_conv(x) 94 | if self.if_shortcut: 95 | x = paddle.add(inputs, x) 96 | return x 97 | 98 | 99 | class SEModule(nn.Layer): 100 | def __init__(self, channel, reduction=4): 101 | super(SEModule, self).__init__() 102 | self.avg_pool = AdaptiveAvgPool2D(1) 103 | self.conv1 = Conv2D( 104 | in_channels=channel, 105 | out_channels=channel // reduction, 106 | kernel_size=1, 107 | stride=1, 108 | padding=0) 109 | 110 | self.conv2 = Conv2D( 111 | in_channels=channel // reduction, 112 | out_channels=channel, 113 | kernel_size=1, 114 | stride=1, 115 | padding=0) 116 | 117 | def forward(self, inputs): 118 | outputs = self.avg_pool(inputs) 119 | outputs = self.conv1(outputs) 120 | outputs = F.relu(outputs) 121 | outputs = self.conv2(outputs) 122 | outputs = F.hardsigmoid(outputs, slope=0.2, offset=0.5) 123 | return paddle.multiply(x=inputs, y=outputs) 124 | 125 | # Basic = ResidualUnit(in_c=80,mid_c=480,out_c=80,filter_size=3,stride=1,use_se=False) 126 | # params_info = paddle.summary(Basic,(1,80,64,64)) 127 | # print(params_info) -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/head.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle import ParamAttr 5 | from paddle.regularizer import L2Decay 6 | 7 | def batch_norm(ch, 8 | norm_type='bn', 9 | norm_decay=0., 10 | freeze_norm=False, 11 | initializer=None, 12 | data_format='NCHW'): 13 | if norm_type == 'sync_bn': 14 | batch_norm = nn.SyncBatchNorm 15 | else: 16 | batch_norm = nn.BatchNorm2D 17 | 18 | norm_lr = 0. if freeze_norm else 1. 19 | weight_attr = ParamAttr( 20 | initializer=initializer, 21 | learning_rate=norm_lr, 22 | regularizer=L2Decay(norm_decay), 23 | trainable=False if freeze_norm else True) 24 | bias_attr = ParamAttr( 25 | learning_rate=norm_lr, 26 | regularizer=L2Decay(norm_decay), 27 | trainable=False if freeze_norm else True) 28 | 29 | norm_layer = batch_norm( 30 | ch, 31 | weight_attr=weight_attr, 32 | bias_attr=bias_attr, 33 | data_format=data_format) 34 | 35 | norm_params = norm_layer.parameters() 36 | if freeze_norm: 37 | for param in norm_params: 38 | param.stop_gradient = True 39 | 40 | return norm_layer 41 | 42 | class ConvBNLayer(nn.Layer): 43 | def __init__(self, 44 | ch_in, 45 | ch_out, 46 | filter_size=3, 47 | stride=1, 48 | groups=1, 49 | padding=0, 50 | norm_type='bn', 51 | norm_decay=0., 52 | act="leaky", 53 | freeze_norm=False, 54 | data_format='NCHW', 55 | name=''): 56 | 57 | super(ConvBNLayer, self).__init__() 58 | 59 | self.conv = nn.Conv2D( 60 | in_channels=ch_in, 61 | out_channels=ch_out, 62 | kernel_size=filter_size, 63 | stride=stride, 64 | padding=padding, 65 | groups=groups, 66 | data_format=data_format, 67 | bias_attr=False) 68 | self.batch_norm = batch_norm( 69 | ch_out, 70 | norm_type=norm_type, 71 | norm_decay=norm_decay, 72 | freeze_norm=freeze_norm, 73 | data_format=data_format) 74 | self.act = act 75 | 76 | def forward(self, inputs): 77 | out = self.conv(inputs) 78 | out = self.batch_norm(out) 79 | if self.act == 'leaky': 80 | out = F.leaky_relu(out, 0.1) 81 | return out 82 | 83 | class YOLOv3Head(nn.Layer): 84 | def __init__(self, 85 | in_channels=[512, 256, 128], 86 | anchors=[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], 87 | [59, 119], [116, 90], [156, 198], [373, 326]], 88 | anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]], 89 | num_classes=80): 90 | 91 | super(YOLOv3Head, self).__init__() 92 | self.in_channels = in_channels 93 | self.num_classes = num_classes 94 | self.anchors = [[anchors[i] for i in mask] for mask in anchor_masks] 95 | self.num_outputs = len(self.anchors) 96 | self.yolo_outputs = [] 97 | for i in range(len(self.anchors)): 98 | num_filters = len(self.anchors[i]) * (self.num_classes + 5) 99 | tip = ConvBNLayer( 100 | ch_in=self.in_channels[i], 101 | ch_out=self.in_channels[i] * 2, 102 | filter_size=3, 103 | padding=1) 104 | 105 | conv = nn.Conv2D( 106 | in_channels=self.in_channels[i]* 2, 107 | out_channels=num_filters, 108 | kernel_size=1, 109 | stride=1, 110 | padding=0) 111 | 112 | yolo_output = self.add_sublayer("yolo_head.tip.{}".format(i), tip) 113 | self.yolo_outputs.append(yolo_output) 114 | yolo_output = self.add_sublayer("yolo_head.output.{}".format(i), conv) 115 | self.yolo_outputs.append(yolo_output) 116 | 117 | def forward(self, feats): 118 | assert len(feats) == len(self.anchors) 119 | yolo_outputs = [] 120 | for i, feat in enumerate(feats): 121 | yolo_output = self.yolo_outputs[2*i](feat) 122 | yolo_output = self.yolo_outputs[2*i+1](yolo_output) 123 | yolo_outputs.append(yolo_output) 124 | 125 | return yolo_outputs 126 | 127 | n = YOLOv3Head() -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/neck.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle import ParamAttr 5 | from paddle.regularizer import L2Decay 6 | 7 | def batch_norm(ch, 8 | norm_type='bn', 9 | norm_decay=0., 10 | freeze_norm=False, 11 | initializer=None, 12 | data_format='NCHW'): 13 | if norm_type == 'sync_bn': 14 | batch_norm = nn.SyncBatchNorm 15 | else: 16 | batch_norm = nn.BatchNorm2D 17 | 18 | norm_lr = 0. if freeze_norm else 1. 19 | weight_attr = ParamAttr( 20 | initializer=initializer, 21 | learning_rate=norm_lr, 22 | regularizer=L2Decay(norm_decay), 23 | trainable=False if freeze_norm else True) 24 | bias_attr = ParamAttr( 25 | learning_rate=norm_lr, 26 | regularizer=L2Decay(norm_decay), 27 | trainable=False if freeze_norm else True) 28 | 29 | norm_layer = batch_norm( 30 | ch, 31 | weight_attr=weight_attr, 32 | bias_attr=bias_attr, 33 | data_format=data_format) 34 | 35 | norm_params = norm_layer.parameters() 36 | if freeze_norm: 37 | for param in norm_params: 38 | param.stop_gradient = True 39 | 40 | return norm_layer 41 | 42 | class ConvBNLayer(nn.Layer): 43 | def __init__(self, 44 | ch_in, 45 | ch_out, 46 | filter_size=3, 47 | stride=1, 48 | groups=1, 49 | padding=0, 50 | norm_type='bn', 51 | norm_decay=0., 52 | act="leaky", 53 | freeze_norm=False, 54 | data_format='NCHW', 55 | name=''): 56 | 57 | super(ConvBNLayer, self).__init__() 58 | 59 | self.conv = nn.Conv2D( 60 | in_channels=ch_in, 61 | out_channels=ch_out, 62 | kernel_size=filter_size, 63 | stride=stride, 64 | padding=padding, 65 | groups=groups, 66 | data_format=data_format, 67 | bias_attr=False) 68 | self.batch_norm = batch_norm( 69 | ch_out, 70 | norm_type=norm_type, 71 | norm_decay=norm_decay, 72 | freeze_norm=freeze_norm, 73 | data_format=data_format) 74 | self.act = act 75 | 76 | def forward(self, inputs): 77 | out = self.conv(inputs) 78 | out = self.batch_norm(out) 79 | if self.act == 'leaky': 80 | out = F.leaky_relu(out, 0.1) 81 | return out 82 | 83 | class YoloDetBlock(nn.Layer): 84 | def __init__(self, 85 | ch_in, 86 | channel, 87 | name): 88 | 89 | super(YoloDetBlock, self).__init__() 90 | self.ch_in = ch_in 91 | self.channel = channel 92 | 93 | conv_def = [ 94 | [ch_in, channel, 1], 95 | [channel, channel * 2, 3], 96 | [channel * 2, channel, 1], 97 | [channel, channel * 2, 3], 98 | [channel * 2, channel, 1], 99 | ] 100 | 101 | self.conv_module = nn.Sequential() 102 | for idx, (ch_in, ch_out, filter_size) in enumerate(conv_def): 103 | self.conv_module.add_sublayer( 104 | name+"{}".format(idx), 105 | ConvBNLayer( 106 | ch_in=ch_in, 107 | ch_out=ch_out, 108 | filter_size=filter_size, 109 | padding=(filter_size - 1) // 2)) 110 | 111 | def forward(self, inputs): 112 | route = self.conv_module(inputs) 113 | return route 114 | 115 | class YOLOv3FPN(nn.Layer): 116 | 117 | def __init__(self,in_channels=[256, 512, 1024]): 118 | 119 | super(YOLOv3FPN, self).__init__() 120 | self.in_channels = in_channels 121 | self.num_blocks = len(in_channels) 122 | self._out_channels = [] 123 | self.yolo_blocks = [] 124 | self.routes = [] 125 | for i in range(self.num_blocks): 126 | name = 'yolo_block.{}'.format(i) 127 | in_channel = in_channels[-i - 1] 128 | if i > 0: 129 | in_channel += 512 // (2**i) 130 | yolo_block = self.add_sublayer( 131 | name, 132 | YoloDetBlock( 133 | in_channel, 134 | channel=512 // (2**i), 135 | name=name)) 136 | 137 | self.yolo_blocks.append(yolo_block) 138 | 139 | if i < self.num_blocks - 1: 140 | name = 'yolo_transition.{}'.format(i) 141 | route = self.add_sublayer( 142 | name, 143 | ConvBNLayer( 144 | ch_in=512 // (2**i), 145 | ch_out=256 // (2**i), 146 | filter_size=1, 147 | stride=1, 148 | padding=0, 149 | name=name)) 150 | self.routes.append(route) 151 | 152 | def forward(self, blocks): 153 | blocks = blocks[::-1] 154 | yolo_feats = [] 155 | for i, block in enumerate(blocks): 156 | if i > 0: 157 | block = paddle.concat([route, block], axis=1) 158 | route = self.yolo_blocks[i](block) 159 | yolo_feats.append(route) 160 | if i < self.num_blocks - 1: 161 | route = self.routes[i](route) 162 | route = F.interpolate(route, scale_factor=2.) 163 | else: 164 | return yolo_feats 165 | 166 | 167 | -------------------------------------------------------------------------------- /第05讲 实战:PaddleX实现垃圾分类/README.md: -------------------------------------------------------------------------------- 1 | ## PaddleX实现垃圾分类 2 | 3 | ### B站教程地址 4 | 5 | https://www.bilibili.com/video/BV18b4y1J7a6/ 6 | 7 | ### PaddleX的安装 8 | 9 | ``` 10 | pip install paddlex==2.1.0 -i https://mirror.baidu.com/pypi/simple 11 | ``` 12 | 13 | 因为**PaddleX依赖于pycocotools**,如果报错: 14 | 15 | ``` 16 | Microsoft Visual C++ 14.0 is required 17 | ``` 18 | 19 | 则需要安装相应工具,下载链接如下: 20 | 21 | 链接:https://pan.baidu.com/s/17pSEX9ZO28_OIPdaeNAe3A 22 | 提取码:xhu4 23 | 24 | **如果出现下载预训练模型报错** 25 | 26 | ``` 27 | SSLError("Can't connect to HTTPS URL because the SSL module is not available) 28 | ``` 29 | 需要安装OpenSLL工具,下载链接如下: 30 | 31 | 链接:[https://pan.baidu.com/s/1Z3F9cIH3-6QrsqnAoK-pBw](https://pan.baidu.com/s/1Z3F9cIH3-6QrsqnAoK-pBw) 32 | 提取码:ucko 33 | 34 | ### 数据集的准备 35 | 36 | 数据集下载链接: 37 | 38 | 链接:https://pan.baidu.com/s/1ZSHQft4eIpYHliKRxZcChQ 39 | 提取码:hce7 40 | 41 | 本次实战为**图片分类任务**,**数据集结构**如下: 42 | 43 |
44 | 45 |
46 | 47 | 分别为分类的图片文件夹,建议有**一个类别就建一个文件夹**,方便管理。训练集和评价集的标签文件,格式如下: 48 | 49 | **./3/933.jpg 3** 50 | **./2/1670.jpg 2** 51 | **./2/2175.jpg 2** 52 | **./1/934.jpg 1** 53 | **./1/1653.jpg 1** 54 | 55 | **...** 56 | 57 | 前面为**图片的相对路径**,后面为**对应的标签类别**。labels.txt存放对应的标签,格式如下: 58 | 59 | **有害垃圾** 60 | **可回收垃圾** 61 | **厨房垃圾** 62 | **其他垃圾** 63 | 64 | 提供文件重命名的代码,**用于每个文件夹里面图片的重命名** 65 | 66 | ``` 67 | import os 68 | 69 | def rename(): 70 | res = os.listdir('./') 71 | for a in res: 72 | i = 0 73 | flag = os.path.isdir(a) 74 | if(flag == False): 75 | continue 76 | path=a 77 | filelist=os.listdir(path)#该文件夹下所有的文件(包括文件夹) 78 | for files in filelist:#遍历所有文件 79 | i=i+1 80 | Olddir=os.path.join(path,files);#原来的文件路径 81 | if os.path.isdir(Olddir):#如果是文件夹则跳过 82 | continue 83 | filename=os.path.splitext(files)[0];#文件名 84 | filetype=os.path.splitext(files)[1];#文件扩展名 85 | Newdir=os.path.join(path,str(i)+filetype);#新的文件路径 86 | os.rename(Olddir,Newdir)#重命名 87 | rename() 88 | 89 | ``` 90 | 91 | 提供**生成train.txt和eval.txt文件**的代码,分类的**比例为5:1** 92 | 93 | ``` 94 | import os 95 | import random 96 | 97 | def ReadFileDatas(): 98 | FileNamelist = [] 99 | file = open('train.txt','r+') 100 | for line in file: 101 | line=line.strip('\n') #删除每一行的\n 102 | FileNamelist.append(line) 103 | #print('len ( FileNamelist ) = ' ,len(FileNamelist)) 104 | file.close() 105 | return FileNamelist 106 | 107 | def WriteDatasToFile(listInfo): 108 | file_handle_train=open('train.txt',mode='w') 109 | file_handle_eval = open("eval.txt",mode='w') 110 | i = 0 111 | for idx in range(len(listInfo)): 112 | str = listInfo[idx] 113 | #查找最后一个 “_”的位置 114 | ndex = str.rfind('_') 115 | #print('ndex = ',ndex) 116 | #截取字符串 117 | str_houZhui = str[(ndex+1):] 118 | #print('str_houZhui = ',str_houZhui) 119 | str_Result = str + '\n' #+ str_houZhui+'\n' 120 | #print(str_Result) 121 | if(i%6 != 0): 122 | file_handle_train.write(str_Result) 123 | else: 124 | file_handle_eval.write(str_Result) 125 | i += 1 126 | file_handle_train.close() 127 | file_handle_eval.close() 128 | 129 | path = './' 130 | res = os.listdir(path) 131 | print(res) 132 | with open("train.txt","w") as f: 133 | for i in res: 134 | if(os.path.isdir(i)): 135 | path1 = path + i 136 | res2 = os.listdir(path1) 137 | for j in res2: 138 | f.write(path1+"/"+j+" " + i +'\n') 139 | 140 | listFileInfo = ReadFileDatas() 141 | #打乱列表中的顺序 142 | random.shuffle(listFileInfo) 143 | WriteDatasToFile(listFileInfo) 144 | ``` 145 | 146 | ### 模型训练 147 | 148 | 借助于**PaddleX**,模型训练变得非常简单,主要分为**数据集定义,数据增强算子定义,模型定义和模型训练**四个步骤: 149 | 150 | ``` 151 | from paddlex import transforms as T 152 | import paddlex as pdx 153 | 154 | train_transforms = T.Compose([ #定义训练集的数据增强算子 155 | T.RandomCrop(crop_size=224), 156 | T.RandomHorizontalFlip(), 157 | T.Normalize()]) 158 | 159 | eval_transforms = T.Compose([ #定义评价集的数据增强算子 160 | T.ResizeByShort(short_size=256), 161 | T.CenterCrop(crop_size=224), 162 | T.Normalize() 163 | ]) 164 | 165 | train_dataset = pdx.datasets.ImageNet( #定义训练集 166 | data_dir='rubbish', 167 | file_list='rubbish/train.txt', 168 | label_list='rubbish/labels.txt', 169 | transforms=train_transforms, 170 | shuffle=True) 171 | eval_dataset = pdx.datasets.ImageNet( #定义评价集 172 | data_dir='rubbish', 173 | file_list='rubbish/eval.txt', 174 | label_list='rubbish/labels.txt', 175 | transforms=eval_transforms) 176 | 177 | num_classes = len(train_dataset.labels) 178 | model = pdx.cls.MobileNetV3_small(num_classes=num_classes) #定义分类模型 179 | 180 | model.train(num_epochs=10, #模型训练 181 | train_dataset=train_dataset, 182 | train_batch_size=64, 183 | eval_dataset=eval_dataset, 184 | lr_decay_epochs=[4, 6, 8], 185 | save_dir='output/mobilenetv3_small', 186 | use_vdl=True) 187 | ``` 188 | 189 | 具体参数的含义可以参照**PaddleX的[Github](https://github.com/PaddlePaddle/PaddleX)文档**,在B站视频中也做了详细的讲解。 190 | 191 | ### 模型的预测 192 | 193 | ``` 194 | import paddlex as pdx 195 | model = pdx.load_model('output/mobilenetv3_small/best_model') 196 | result = model.predict('188.jpg') 197 | print("Predict Result: ", result) 198 | ``` 199 | 200 | 可以观察输出的结果是否正确。 201 | 202 | ### 模型训练的可视化 203 | 204 | ``` 205 | visualdl --logdir output/mobilenetv3_small --port 8001 206 | ``` 207 | 208 | 打开浏览器输出网址,可以看到训练的**各个参数曲线**。 -------------------------------------------------------------------------------- /第09讲 MobilenetV3网络详解/README.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | 3 | **Mobilenet**是由**Google**公司创造的网络系列,目前已经发布至**V3**版本,每一次版本更新都是**在前一次网络上的优化修改**。**Mobilenet**主打的是**轻量级网络**,也就说网络参数量较少,执行速度较快,也**更容易部署到终端设备上**。在移动端和嵌入式设备上也有了很多的应用。 4 | 5 | **MobilenetV3**对**MobilenetV2**进行了一系列小的修改,实现了**精度的再次突破,速度也有所提升。** 6 | 7 | ## 主要结构 8 | 9 | ### 深度可分离卷积 10 | 11 |
12 | 13 |
14 | 15 | **MobilenetV3**的主体部分大量使用了**深度可分离卷积**,在上一讲中我们做了详细的介绍。再次指出,这种卷积结构**极大地减少了参数量**,对于轻量级的网络是非常有利的。 16 | 17 | ### SE注意力机制 18 | 19 |
20 | 21 |
22 | 23 | 在**MobilenetV3**的基础结构中,使用了**SE注意力机制**,这一点我们上一讲也做了介绍。因为**SE注意力机制会增加少量的参数**,但对于精度有提升,所有**MobilenetV3中对某些层加入了SE注意力机制,追求精度和参数量的平衡**。而且对初始的注意力机制做了一定的修改,主要体现在**卷积层**和**激活函数**。 24 | 25 | ### 新型的激活函数 26 | 27 | **MobilenetV3**中使用了**Hardswish**激活函数,代替了**Swish**激活。 28 | 29 |
30 | 31 |
32 | 33 | 从公式上来看,**Hardswish**代替了指数函数,从而**降低了计算的成本**,使模型轻量化。 34 | 35 |
36 | 37 |
38 | 39 | 做出**函数图像和梯度图像**,可以看出**原函数非常接近**。在梯度图上**Hardswish**存在**突变**,这对于训练是**不利**的,而**swish**梯度变化**平滑**。也就是说**Hardswish加快了运算速度**,但是**不利于提高精度**。**MobilenetV3**经过多次实验,发现**Hardswish**在**更深的网络**中精度损失较小,最终选用在网络的**前半部分**使用了**Relu**激活,在**深层网络**中使用了**Hardswish**激活。 40 | 41 | ### 修改了尾部结构 42 | 43 | **MobilenetV3**修改了**MobilenetV2**的尾部结构,具体修改如下: 44 | 45 |
46 | 47 |
48 | 49 | **MobilenetV2**最后的尾部使用了**四层卷积层再接了一个平均池化**,**MobilenetV3**仅通过**一个卷积层**修改通道数后,直接接了**平均池化层**。这也**大大减少了网络的参数**量,在实验中发现,**精度并没有降低**。 50 | 51 | ## 整体网络 52 | 53 | 经过以上的一些小的修改后,**MobilenetV3**的**整体网络**形式就非常清晰了,它的**通用结构单元**如下: 54 | 55 |
56 | 57 |
58 | 59 | 整体网络就是由**多个这样的单元堆叠**而成。**MobilenetV3**有**large**和**small**两个版本,我们以**large**为例分析。 60 | 61 |
62 | 63 |
64 | 65 | 表中**input**表示输出的**shape**,**exp size**表示扩大的**通道数**,**out**表示**输出通道数**,**SE**表示**是否使用SE注意力机制**,**NL**表示使用的**激活函数**,**S**表示卷积的**步长**。 66 | 67 | **bneck**就是第一个图所示的格式,可以看到中间**重复使用了多次**。先使用一个卷积层,把通道数扩充到**16**,之后通过多个**bneck**充分提取特征,然后接着使用一个**尾部结构**,最后输出一个**类别数的矩阵**。因为目前写论文通常使用的是**imagenet**数据集,是一个**1000类别**的庞大分类数据集,所以官方网络一般最后输出的维度都是**1000**。 68 | 69 | ## 使用PaddleClas训练MobilenetV3 70 | 71 | **PaddleClas**是依赖于**paddle**的视觉分类套件,其中集成了很多**分类**的典型网络,我们使用**PaddleClas**中的**MobilenetV3**训练一下**垃圾分类任务**。 72 | 73 | **PaddleClas**中**MobileNetV3**整体的代码实现如下: 74 | 75 | ``` 76 | class MobileNetV3(TheseusLayer): 77 | """ 78 | MobileNetV3 79 | Args: 80 | config: list. MobileNetV3 depthwise blocks config. 81 | scale: float=1.0. The coefficient that controls the size of network parameters. 82 | class_num: int=1000. The number of classes. 83 | inplanes: int=16. The output channel number of first convolution layer. 84 | class_squeeze: int=960. The output channel number of penultimate convolution layer. 85 | class_expand: int=1280. The output channel number of last convolution layer. 86 | dropout_prob: float=0.2. Probability of setting units to zero. 87 | Returns: 88 | model: nn.Layer. Specific MobileNetV3 model depends on args. 89 | """ 90 | 91 | def __init__(self, 92 | config, 93 | scale=1.0, 94 | class_num=1000, 95 | inplanes=STEM_CONV_NUMBER, 96 | class_squeeze=LAST_SECOND_CONV_LARGE, 97 | class_expand=LAST_CONV, 98 | dropout_prob=0.2, 99 | return_patterns=None): 100 | super().__init__() 101 | 102 | self.cfg = config 103 | self.scale = scale 104 | self.inplanes = inplanes 105 | self.class_squeeze = class_squeeze 106 | self.class_expand = class_expand 107 | self.class_num = class_num 108 | 109 | self.conv = ConvBNLayer( 110 | in_c=3, 111 | out_c=_make_divisible(self.inplanes * self.scale), 112 | filter_size=3, 113 | stride=2, 114 | padding=1, 115 | num_groups=1, 116 | if_act=True, 117 | act="hardswish") 118 | 119 | self.blocks = nn.Sequential(* [ 120 | ResidualUnit( 121 | in_c=_make_divisible(self.inplanes * self.scale if i == 0 else 122 | self.cfg[i - 1][2] * self.scale), 123 | mid_c=_make_divisible(self.scale * exp), 124 | out_c=_make_divisible(self.scale * c), 125 | filter_size=k, 126 | stride=s, 127 | use_se=se, 128 | act=act) for i, (k, exp, c, se, act, s) in enumerate(self.cfg) 129 | ]) 130 | 131 | self.last_second_conv = ConvBNLayer( 132 | in_c=_make_divisible(self.cfg[-1][2] * self.scale), 133 | out_c=_make_divisible(self.scale * self.class_squeeze), 134 | filter_size=1, 135 | stride=1, 136 | padding=0, 137 | num_groups=1, 138 | if_act=True, 139 | act="hardswish") 140 | 141 | self.avg_pool = AdaptiveAvgPool2D(1) 142 | 143 | self.last_conv = Conv2D( 144 | in_channels=_make_divisible(self.scale * self.class_squeeze), 145 | out_channels=self.class_expand, 146 | kernel_size=1, 147 | stride=1, 148 | padding=0, 149 | bias_attr=False) 150 | 151 | self.hardswish = nn.Hardswish() 152 | self.dropout = Dropout(p=dropout_prob, mode="downscale_in_infer") 153 | self.flatten = nn.Flatten(start_axis=1, stop_axis=-1) 154 | 155 | self.fc = Linear(self.class_expand, class_num) 156 | if return_patterns is not None: 157 | self.update_res(return_patterns) 158 | self.register_forward_post_hook(self._return_dict_hook) 159 | 160 | def forward(self, x): 161 | x = self.conv(x) 162 | x = self.blocks(x) 163 | x = self.last_second_conv(x) 164 | x = self.avg_pool(x) 165 | x = self.last_conv(x) 166 | x = self.hardswish(x) 167 | x = self.dropout(x) 168 | x = self.flatten(x) 169 | x = self.fc(x) 170 | 171 | return x 172 | ``` 173 | 174 | 具体训练也很简单,我们只**需要修改相应的配置文件**,存放在**ppcls/config目录下**。对于训练自己的数据集,**着重需要修改数据集的相关参数,batch_size以及输出图像的大小等**,其他根据自己的需求进行修改。 175 | 176 | 训练的方法是使用命令行命令。 177 | 178 | **训练命令**: 179 | 180 | ``` 181 | python tools/train.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Arch.pretrained=True -o Global.device=gpu 182 | ``` 183 | 184 | **断点训练命令**: 185 | 186 | ``` 187 | python tools/train.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Global.checkpoints="./output/MobileNetV3_large_x1_0/epoch_5" -o Global.device=gpu 188 | ``` 189 | 190 | **预测命令**: 191 | 192 | ``` 193 | python tools/infer.py -c ./ppcls/configs/quick_start/MobileNetV3_large_x1_0.yaml -o Infer.infer_imgs=./188.jpg -o Global.pretrained_model=./output/MobileNetV3_large_x1_0/best_model 194 | ``` 195 | 196 | ## 参考资料 197 | 198 | https://arxiv.org/abs/1905.02244 199 | 200 | https://github.com/PaddlePaddle/PaddleClas 201 | 202 | https://blog.csdn.net/qq_42617455/article/details/108165206 203 | -------------------------------------------------------------------------------- /第07讲 实战:手写数字识别/README.md: -------------------------------------------------------------------------------- 1 | ## 任务介绍 2 | 3 | **手写数字识别**是计算机视觉的一个**经典项目**,因为**手写数字的随机性**,使用传统的计算机视觉技术**难以找到数字共有特征**。在计算机视觉发展的初期,手写数字识别成为一大难题。 4 | 5 | 从我们之前讲解的视觉任务分类来看,**手写数字识别是典型的分类任务**,输入一张图片进行**十分类**。在现实生活中,手写数字识别也有非常多的应用场景。如下图,我们看到的**邮编的识别**,可以极大地推动产业自动化,使用卷积神经网络实现的**精度甚至可以超越人类**。 6 | 7 | 本次任务就是想建立一个模型,输入一张手写数字的图片,就能输出一个正确的分类结果。通过这样的一个实战项目,可以很好地帮我们**巩固和理解我们之前讲过的卷积、池化等常用操作**,也可以温习一下深度学习的基本流程。 8 | 9 |
10 | 11 |
12 | 13 | ## 数据准备 14 | 15 | 手写数字识别有通用的数据集**MNIST**,其中包含已经标注好的几万张手写数字,并且分好了训练集和评价集。如果我们对其中的一张图片进行**可视化**,可以看到这样的画面: 16 | 17 |
18 | 19 |
20 | 21 | 图像的shape为(1,28,28),是**单通道图**,图像的大小仅为**28*28**,它的**标注为7**。 22 | 23 | 通常对于一般项目来说,需要自己**手写一个Dataloader来依次加载数据,返回图片和标注,供给训练的接口用于训练**。这里考虑到我们入门的原因,直接使用写好的API。有兴趣的同学可以自己尝试不使用高级API,自己下载好压缩包手写一下Dataloader。 24 | 25 | ``` 26 | train_loader = paddle.io.DataLoader(MNIST(mode='train', transform=ToTensor()), batch_size=10, shuffle=True) 27 | valid_loader = paddle.io.DataLoader(MNIST(mode='test', transform=ToTensor()), batch_size=10) 28 | ``` 29 | 30 | 通过上面包装好的API,我们就加载好了**训练集**和**评价集**,可以供训练接口调用。 31 | 32 | ## 网络搭建 33 | 34 | 准备好数据之后,第二部也就是**搭建卷积神经网络**,卷积神经网络直接影响着模型的精度,这一步也是**最为关键的一个环节**。本次实战中,我们默认使用**LeNet**。**LeNet**是最早的卷积神经网络之一,**诞生于1998年,在手写数字识别任务中取得了巨大成功**。 35 | 36 |
37 | 38 |
39 | 40 | 它的网络结构也非常简单,基本上为**一个卷积层接着一个池化层**,最后通过两个全连接层输出一个[1,10]的矩阵。全连接层我们之前没有介绍过,它通常用于**拟合一些批量数据,比如有很多散点,拟合出一条曲线**。它的结构如下: 41 | 42 |
43 | 44 |
45 | 46 | 也就是说**每一个输出和前面一层的所有参数都相关**,它的数学表达其实就是**乘上一个变换矩阵再加上偏差**,得到输出矩阵。**为什么图像中大量使用卷积层,很少使用全连接层呢?这边留给大家课后自己思考。** 47 | 48 | **LeNet**使用**Paddle**复现代码如下: 49 | 50 | ``` 51 | import paddle 52 | import numpy as np 53 | from paddle.nn import Conv2D, MaxPool2D, Linear 54 | import paddle.nn.functional as F 55 | 56 | # 定义 LeNet 网络结构 57 | class LeNet(paddle.nn.Layer): 58 | def __init__(self, num_classes=1): 59 | super(LeNet, self).__init__() 60 | self.conv1 = Conv2D(in_channels=1, out_channels=6, kernel_size=5) 61 | self.max_pool1 = MaxPool2D(kernel_size=2, stride=2) 62 | self.conv2 = Conv2D(in_channels=6, out_channels=16, kernel_size=5) 63 | self.max_pool2 = MaxPool2D(kernel_size=2, stride=2) 64 | self.conv3 = Conv2D(in_channels=16, out_channels=120, kernel_size=4) 65 | self.fc1 = Linear(in_features=120, out_features=64) 66 | self.fc2 = Linear(in_features=64, out_features=num_classes) 67 | def forward(self, x): #[N,1,28,28] 68 | x = self.conv1(x) #[N,6,24,24] 69 | x = F.sigmoid(x) #[N,6,24,24] 70 | x = self.max_pool1(x) #[N,6,12,12] 71 | x = F.sigmoid(x) #[N,6,12,12] 72 | x = self.conv2(x) #[N,16,8,8] 73 | x = self.max_pool2(x) #[N,16,4,4] 74 | x = self.conv3(x) #[N,120,1,1] 75 | x = paddle.reshape(x, [x.shape[0], -1]) #[N,120] 76 | x = self.fc1(x) #[N,64] 77 | x = F.sigmoid(x) #[N,64] 78 | x = self.fc2(x) #[N,10] 79 | return x 80 | ``` 81 | 82 | Paddle使用**动态图的这种写法非常清晰**,定义一个类体,在初始化函数内写好需要使用的层,**需要特别注意好输入输出的通道数,卷积核的大小这些参数,如果稍不注意就会出现维度上的错误**。在这边定义好之后,我们再写forward函数,**forward函数就是之后我们传入图像后真正执行的运算。** 83 | 84 | 为了帮助大家理解,再详细解释一下执行流程。首先我们**实例化类体**。 85 | 86 | ``` 87 | model = LeNet(num_classes=10) 88 | ``` 89 | 90 | 实例化的时候,类体**自动地执行init()初始化函数**,init()函数里面又实例化了**Conv2D,MaxPool2D**,这些其实都是**类体**,这些类体和LeNet一样,**也有init()和forward函数**,在初始化函数中都进行了相应的实例化。**实例化的过程,并没有真正开始运算**,只是定义好了我想要使用的层。 91 | 92 | ``` 93 | output = model(img) 94 | ``` 95 | 96 | 当我再次运行上面的代码后,相当于**调用**了这个**类体**,并且输入了**img**,这时候类体会自动调用**call()函数**,那forward函数为什么会执行呢?原因就在于所有的运算都继承了**paddle.nn.Layer母类**,母类中将**forward函数写在了call()下面**,那么就相当于调用**LeNet**这个类体的时候,**自动调用了forward函数,这时候也就开始了真正的运算过程**。 97 | 98 | 整个过程希望大家反复推敲,知道彻底理解为止。不难发现,这样的建立网络的形式,可以**不停地嵌套**,这是非常清晰的形式,我们之后讲解复杂模型的时候这样的优势就会体现出来。 99 | 100 | ## 模型训练 101 | 102 | ``` 103 | # -*- coding: utf-8 -*- 104 | # LeNet 识别手写数字 105 | import imp 106 | import paddle 107 | import numpy as np 108 | import paddle 109 | from model import LeNet 110 | from paddle.vision.transforms import ToTensor 111 | from paddle.vision.datasets import MNIST 112 | 113 | 114 | def train(model, opt, train_loader, valid_loader): 115 | use_gpu = True 116 | paddle.device.set_device('gpu:0') if use_gpu else paddle.device.set_device('cpu') 117 | print('start training ... ') 118 | model.train() 119 | for epoch in range(EPOCH_NUM): 120 | for batch_id, data in enumerate(train_loader()): 121 | img = data[0] #[10,1,28,28] 122 | label = data[1] #[10,1] 123 | # 计算模型输出 124 | logits = model(img) 125 | # 计算损失函数 126 | loss_func = paddle.nn.CrossEntropyLoss(reduction='none') 127 | loss = loss_func(logits, label) 128 | avg_loss = paddle.mean(loss) 129 | 130 | if batch_id % 500 == 0: 131 | print("epoch: {}, batch_id: {}, loss is: {:.4f}".format(epoch+1, batch_id, float(avg_loss.numpy()))) 132 | avg_loss.backward() 133 | opt.step() 134 | opt.clear_grad() 135 | 136 | model.eval() 137 | accuracies = [] 138 | losses = [] 139 | for batch_id, data in enumerate(valid_loader()): 140 | img = data[0] 141 | label = data[1] 142 | # 计算模型输出 143 | logits = model(img) 144 | # 计算损失函数 145 | loss_func = paddle.nn.CrossEntropyLoss(reduction='none') 146 | loss = loss_func(logits, label) 147 | acc = paddle.metric.accuracy(logits, label) 148 | accuracies.append(acc.numpy()) 149 | losses.append(loss.numpy()) 150 | print("[validation] accuracy/loss: {:.4f}/{:.4f}".format(np.mean(accuracies), np.mean(losses))) 151 | model.train() 152 | 153 | # 保存模型参数 154 | paddle.save(model.state_dict(), 'mnist.pdparams') 155 | 156 | 157 | model = LeNet(num_classes=10) 158 | EPOCH_NUM = 5 159 | opt = paddle.optimizer.Momentum(learning_rate=0.001, parameters=model.parameters()) 160 | train_loader = paddle.io.DataLoader(MNIST(mode='train', transform=ToTensor()), batch_size=10, shuffle=True) 161 | valid_loader = paddle.io.DataLoader(MNIST(mode='test', transform=ToTensor()), batch_size=10) 162 | train(model, opt, train_loader, valid_loader) 163 | ``` 164 | 165 | 训练的代码我们根据学过的知识,就非常清晰。从**数据集接口获得数据集**,把图像输入到模型中,模型得到一个预测值,使用**CrossEntropyLoss损失函数**计算预测值和标签真实值的**loss**,将**loss反向发聩给网络参数**,最后使用**优化器**修正参数,降低**loss**。 166 | 167 | 需要注意的是**CrossEntropyLoss损失函数自带softmax**,分类问题最后都需要一个**softmax激活函数**,**把输出的[1,10]矩阵归到[0,1],并且10个数的和为1,也就代表了这张图片为0-9的概率**。 168 | 169 | ## 模型预测 170 | 171 | ``` 172 | import numpy as np 173 | import paddle 174 | from model import LeNet 175 | from paddle.vision.datasets import MNIST 176 | from paddle.vision.transforms import ToTensor 177 | import paddle.nn.functional as F 178 | 179 | valid_loader = MNIST(mode='test', transform=ToTensor()) 180 | img = np.array(valid_loader[0][0]) 181 | 182 | # import matplotlib.pyplot as plt 183 | # plt.imshow(img.squeeze(), cmap='gray') 184 | # plt.show() 185 | 186 | model = LeNet(num_classes=10) 187 | model_dict = paddle.load("mnist.pdparams") 188 | model.set_state_dict(model_dict) 189 | model.eval() 190 | x = valid_loader[0][0].reshape((1,1,28,28)).astype('float32') 191 | result = F.softmax(model(x)) 192 | print(result.numpy()[0]) 193 | ``` 194 | 195 | 训练完模型之后,我们需要**加载模型并且预测**,这里就挑选了评价集中的一张图片预测,看一下输出的结果是否正确。 196 | 197 | ``` 198 | model = LeNet(num_classes=10) 199 | model_dict = paddle.load("mnist.pdparams") 200 | model.set_state_dict(model_dict) 201 | ``` 202 | 203 | **我们使用这样的方法加载模型**,最后预测输出: 204 | 205 | ``` 206 | [7.3181213e-06 1.4578840e-05 3.3818762e-04 2.1557527e-04 2.6723552e-05 207 | 6.7271581e-06 1.3456239e-08 9.9840504e-01 4.1231990e-05 9.4459485e-04] 208 | ``` 209 | 210 | 这也就分别代表**0-9的概率**,7的概率高达99.84%,模型输出正确! 211 | 212 | ## 参考资料 213 | 214 | https://www.paddlepaddle.org.cn/tutorials/projectdetail/2227103 215 | -------------------------------------------------------------------------------- /第11讲 目标检测YOLOv3-neck、head/darknet.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle import ParamAttr 5 | from paddle.regularizer import L2Decay 6 | 7 | def batch_norm(ch, 8 | norm_type='bn', 9 | norm_decay=0., 10 | freeze_norm=False, 11 | initializer=None, 12 | data_format='NCHW'): 13 | if norm_type == 'sync_bn': 14 | batch_norm = nn.SyncBatchNorm 15 | else: 16 | batch_norm = nn.BatchNorm2D 17 | 18 | norm_lr = 0. if freeze_norm else 1. 19 | weight_attr = ParamAttr( 20 | initializer=initializer, 21 | learning_rate=norm_lr, 22 | regularizer=L2Decay(norm_decay), 23 | trainable=False if freeze_norm else True) 24 | bias_attr = ParamAttr( 25 | learning_rate=norm_lr, 26 | regularizer=L2Decay(norm_decay), 27 | trainable=False if freeze_norm else True) 28 | 29 | norm_layer = batch_norm( 30 | ch, 31 | weight_attr=weight_attr, 32 | bias_attr=bias_attr, 33 | data_format=data_format) 34 | 35 | norm_params = norm_layer.parameters() 36 | if freeze_norm: 37 | for param in norm_params: 38 | param.stop_gradient = True 39 | 40 | return norm_layer 41 | 42 | 43 | class ConvBNLayer(nn.Layer): 44 | def __init__(self, 45 | ch_in, 46 | ch_out, 47 | filter_size=3, 48 | stride=1, 49 | groups=1, 50 | padding=0, 51 | norm_type='bn', 52 | norm_decay=0., 53 | act="leaky", 54 | freeze_norm=False, 55 | data_format='NCHW', 56 | name=''): 57 | 58 | super(ConvBNLayer, self).__init__() 59 | 60 | self.conv = nn.Conv2D( 61 | in_channels=ch_in, 62 | out_channels=ch_out, 63 | kernel_size=filter_size, 64 | stride=stride, 65 | padding=padding, 66 | groups=groups, 67 | data_format=data_format, 68 | bias_attr=False) 69 | self.batch_norm = batch_norm( 70 | ch_out, 71 | norm_type=norm_type, 72 | norm_decay=norm_decay, 73 | freeze_norm=freeze_norm, 74 | data_format=data_format) 75 | self.act = act 76 | 77 | def forward(self, inputs): 78 | out = self.conv(inputs) 79 | out = self.batch_norm(out) 80 | if self.act == 'leaky': 81 | out = F.leaky_relu(out, 0.1) 82 | return out 83 | 84 | 85 | class DownSample(nn.Layer): 86 | def __init__(self, 87 | ch_in, 88 | ch_out, 89 | filter_size=3, 90 | stride=2, 91 | padding=1, 92 | norm_type='bn', 93 | norm_decay=0., 94 | freeze_norm=False, 95 | data_format='NCHW'): 96 | 97 | super(DownSample, self).__init__() 98 | 99 | self.conv_bn_layer = ConvBNLayer( 100 | ch_in=ch_in, 101 | ch_out=ch_out, 102 | filter_size=filter_size, 103 | stride=stride, 104 | padding=padding, 105 | norm_type=norm_type, 106 | norm_decay=norm_decay, 107 | freeze_norm=freeze_norm, 108 | data_format=data_format) 109 | self.ch_out = ch_out 110 | 111 | def forward(self, inputs): 112 | out = self.conv_bn_layer(inputs) 113 | return out 114 | 115 | 116 | class BasicBlock(nn.Layer): 117 | def __init__(self, 118 | ch_in, 119 | ch_out, 120 | norm_type='bn', 121 | norm_decay=0., 122 | freeze_norm=False, 123 | data_format='NCHW'): 124 | 125 | super(BasicBlock, self).__init__() 126 | 127 | self.conv1 = ConvBNLayer( 128 | ch_in=ch_in, 129 | ch_out=ch_out, 130 | filter_size=1, 131 | stride=1, 132 | padding=0, 133 | norm_type=norm_type, 134 | norm_decay=norm_decay, 135 | freeze_norm=freeze_norm, 136 | data_format=data_format) 137 | self.conv2 = ConvBNLayer( 138 | ch_in=ch_out, 139 | ch_out=ch_out * 2, 140 | filter_size=3, 141 | stride=1, 142 | padding=1, 143 | norm_type=norm_type, 144 | norm_decay=norm_decay, 145 | freeze_norm=freeze_norm, 146 | data_format=data_format) 147 | 148 | def forward(self, inputs): 149 | conv1 = self.conv1(inputs) 150 | conv2 = self.conv2(conv1) 151 | out = paddle.add(x=inputs, y=conv2) 152 | return out 153 | 154 | 155 | class Blocks(nn.Layer): 156 | def __init__(self, 157 | ch_in, 158 | ch_out, 159 | count, 160 | norm_type='bn', 161 | norm_decay=0., 162 | freeze_norm=False, 163 | name=None, 164 | data_format='NCHW'): 165 | 166 | super(Blocks, self).__init__() 167 | 168 | self.basicblock0 = BasicBlock( 169 | ch_in, 170 | ch_out, 171 | norm_type=norm_type, 172 | norm_decay=norm_decay, 173 | freeze_norm=freeze_norm, 174 | data_format=data_format) 175 | self.res_out_list = [] 176 | for i in range(1, count): 177 | block_name = '{}.{}'.format(name, i) 178 | res_out = self.add_sublayer( 179 | block_name, 180 | BasicBlock( 181 | ch_out * 2, 182 | ch_out, 183 | norm_type=norm_type, 184 | norm_decay=norm_decay, 185 | freeze_norm=freeze_norm, 186 | data_format=data_format)) 187 | self.res_out_list.append(res_out) 188 | self.ch_out = ch_out 189 | 190 | def forward(self, inputs): 191 | y = self.basicblock0(inputs) 192 | for basic_block_i in self.res_out_list: 193 | y = basic_block_i(y) 194 | return y 195 | 196 | 197 | DarkNet_cfg = {53: ([1, 2, 8, 8, 4])} 198 | 199 | class DarkNet(nn.Layer): 200 | __shared__ = ['norm_type', 'data_format'] 201 | 202 | def __init__(self, 203 | depth=53, 204 | freeze_at=-1, 205 | return_idx=[2, 3, 4], 206 | num_stages=5, 207 | norm_type='bn', 208 | norm_decay=0., 209 | freeze_norm=False, 210 | data_format='NCHW'): 211 | 212 | super(DarkNet, self).__init__() 213 | self.depth = depth 214 | self.freeze_at = freeze_at 215 | self.return_idx = return_idx 216 | self.num_stages = num_stages 217 | self.stages = DarkNet_cfg[self.depth][0:num_stages] 218 | 219 | self.conv0 = ConvBNLayer( 220 | ch_in=3, 221 | ch_out=32, 222 | filter_size=3, 223 | stride=1, 224 | padding=1, 225 | norm_type=norm_type, 226 | norm_decay=norm_decay, 227 | freeze_norm=freeze_norm, 228 | data_format=data_format) 229 | 230 | self.downsample0 = DownSample( 231 | ch_in=32, 232 | ch_out=32 * 2, 233 | norm_type=norm_type, 234 | norm_decay=norm_decay, 235 | freeze_norm=freeze_norm, 236 | data_format=data_format) 237 | 238 | self._out_channels = [] 239 | self.darknet_conv_block_list = [] 240 | self.downsample_list = [] 241 | ch_in = [64, 128, 256, 512, 1024] 242 | for i, stage in enumerate(self.stages): 243 | name = 'stage.{}'.format(i) 244 | conv_block = self.add_sublayer( 245 | name, 246 | Blocks( 247 | int(ch_in[i]), 248 | 32 * (2**i), 249 | stage, 250 | norm_type=norm_type, 251 | norm_decay=norm_decay, 252 | freeze_norm=freeze_norm, 253 | data_format=data_format, 254 | name=name)) 255 | self.darknet_conv_block_list.append(conv_block) 256 | if i in return_idx: 257 | self._out_channels.append(64 * (2**i)) 258 | for i in range(num_stages - 1): 259 | down_name = 'stage.{}.downsample'.format(i) 260 | downsample = self.add_sublayer( 261 | down_name, 262 | DownSample( 263 | ch_in=32 * (2**(i + 1)), 264 | ch_out=32 * (2**(i + 2)), 265 | norm_type=norm_type, 266 | norm_decay=norm_decay, 267 | freeze_norm=freeze_norm, 268 | data_format=data_format)) 269 | self.downsample_list.append(downsample) 270 | 271 | def forward(self, inputs): 272 | x = inputs 273 | 274 | out = self.conv0(x) 275 | out = self.downsample0(out) 276 | blocks = [] 277 | for i, conv_block_i in enumerate(self.darknet_conv_block_list): 278 | out = conv_block_i(out) 279 | if i == self.freeze_at: 280 | out.stop_gradient = True 281 | if i in self.return_idx: 282 | blocks.append(out) 283 | if i < self.num_stages - 1: 284 | out = self.downsample_list[i](out) 285 | return blocks 286 | 287 | 288 | -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/darknet.py: -------------------------------------------------------------------------------- 1 | import paddle 2 | import paddle.nn as nn 3 | import paddle.nn.functional as F 4 | from paddle import ParamAttr 5 | from paddle.regularizer import L2Decay 6 | 7 | def batch_norm(ch, 8 | norm_type='bn', 9 | norm_decay=0., 10 | freeze_norm=False, 11 | initializer=None, 12 | data_format='NCHW'): 13 | if norm_type == 'sync_bn': 14 | batch_norm = nn.SyncBatchNorm 15 | else: 16 | batch_norm = nn.BatchNorm2D 17 | 18 | norm_lr = 0. if freeze_norm else 1. 19 | weight_attr = ParamAttr( 20 | initializer=initializer, 21 | learning_rate=norm_lr, 22 | regularizer=L2Decay(norm_decay), 23 | trainable=False if freeze_norm else True) 24 | bias_attr = ParamAttr( 25 | learning_rate=norm_lr, 26 | regularizer=L2Decay(norm_decay), 27 | trainable=False if freeze_norm else True) 28 | 29 | norm_layer = batch_norm( 30 | ch, 31 | weight_attr=weight_attr, 32 | bias_attr=bias_attr, 33 | data_format=data_format) 34 | 35 | norm_params = norm_layer.parameters() 36 | if freeze_norm: 37 | for param in norm_params: 38 | param.stop_gradient = True 39 | 40 | return norm_layer 41 | 42 | 43 | class ConvBNLayer(nn.Layer): 44 | def __init__(self, 45 | ch_in, 46 | ch_out, 47 | filter_size=3, 48 | stride=1, 49 | groups=1, 50 | padding=0, 51 | norm_type='bn', 52 | norm_decay=0., 53 | act="leaky", 54 | freeze_norm=False, 55 | data_format='NCHW', 56 | name=''): 57 | 58 | super(ConvBNLayer, self).__init__() 59 | 60 | self.conv = nn.Conv2D( 61 | in_channels=ch_in, 62 | out_channels=ch_out, 63 | kernel_size=filter_size, 64 | stride=stride, 65 | padding=padding, 66 | groups=groups, 67 | data_format=data_format, 68 | bias_attr=False) 69 | self.batch_norm = batch_norm( 70 | ch_out, 71 | norm_type=norm_type, 72 | norm_decay=norm_decay, 73 | freeze_norm=freeze_norm, 74 | data_format=data_format) 75 | self.act = act 76 | 77 | def forward(self, inputs): 78 | out = self.conv(inputs) 79 | out = self.batch_norm(out) 80 | if self.act == 'leaky': 81 | out = F.leaky_relu(out, 0.1) 82 | return out 83 | 84 | 85 | class DownSample(nn.Layer): 86 | def __init__(self, 87 | ch_in, 88 | ch_out, 89 | filter_size=3, 90 | stride=2, 91 | padding=1, 92 | norm_type='bn', 93 | norm_decay=0., 94 | freeze_norm=False, 95 | data_format='NCHW'): 96 | 97 | super(DownSample, self).__init__() 98 | 99 | self.conv_bn_layer = ConvBNLayer( 100 | ch_in=ch_in, 101 | ch_out=ch_out, 102 | filter_size=filter_size, 103 | stride=stride, 104 | padding=padding, 105 | norm_type=norm_type, 106 | norm_decay=norm_decay, 107 | freeze_norm=freeze_norm, 108 | data_format=data_format) 109 | self.ch_out = ch_out 110 | 111 | def forward(self, inputs): 112 | out = self.conv_bn_layer(inputs) 113 | return out 114 | 115 | 116 | class BasicBlock(nn.Layer): 117 | def __init__(self, 118 | ch_in, 119 | ch_out, 120 | norm_type='bn', 121 | norm_decay=0., 122 | freeze_norm=False, 123 | data_format='NCHW'): 124 | 125 | super(BasicBlock, self).__init__() 126 | 127 | self.conv1 = ConvBNLayer( 128 | ch_in=ch_in, 129 | ch_out=ch_out, 130 | filter_size=1, 131 | stride=1, 132 | padding=0, 133 | norm_type=norm_type, 134 | norm_decay=norm_decay, 135 | freeze_norm=freeze_norm, 136 | data_format=data_format) 137 | self.conv2 = ConvBNLayer( 138 | ch_in=ch_out, 139 | ch_out=ch_out * 2, 140 | filter_size=3, 141 | stride=1, 142 | padding=1, 143 | norm_type=norm_type, 144 | norm_decay=norm_decay, 145 | freeze_norm=freeze_norm, 146 | data_format=data_format) 147 | 148 | def forward(self, inputs): 149 | conv1 = self.conv1(inputs) 150 | conv2 = self.conv2(conv1) 151 | out = paddle.add(x=inputs, y=conv2) 152 | return out 153 | 154 | 155 | class Blocks(nn.Layer): 156 | def __init__(self, 157 | ch_in, 158 | ch_out, 159 | count, 160 | norm_type='bn', 161 | norm_decay=0., 162 | freeze_norm=False, 163 | name=None, 164 | data_format='NCHW'): 165 | 166 | super(Blocks, self).__init__() 167 | 168 | self.basicblock0 = BasicBlock( 169 | ch_in, 170 | ch_out, 171 | norm_type=norm_type, 172 | norm_decay=norm_decay, 173 | freeze_norm=freeze_norm, 174 | data_format=data_format) 175 | self.res_out_list = [] 176 | for i in range(1, count): 177 | block_name = '{}.{}'.format(name, i) 178 | res_out = self.add_sublayer( 179 | block_name, 180 | BasicBlock( 181 | ch_out * 2, 182 | ch_out, 183 | norm_type=norm_type, 184 | norm_decay=norm_decay, 185 | freeze_norm=freeze_norm, 186 | data_format=data_format)) 187 | self.res_out_list.append(res_out) 188 | self.ch_out = ch_out 189 | 190 | def forward(self, inputs): 191 | y = self.basicblock0(inputs) 192 | for basic_block_i in self.res_out_list: 193 | y = basic_block_i(y) 194 | return y 195 | 196 | 197 | DarkNet_cfg = {53: ([1, 2, 8, 8, 4])} 198 | 199 | class DarkNet(nn.Layer): 200 | __shared__ = ['norm_type', 'data_format'] 201 | 202 | def __init__(self, 203 | depth=53, 204 | freeze_at=-1, 205 | return_idx=[2, 3, 4], 206 | num_stages=5, 207 | norm_type='bn', 208 | norm_decay=0., 209 | freeze_norm=False, 210 | data_format='NCHW'): 211 | 212 | super(DarkNet, self).__init__() 213 | self.depth = depth 214 | self.freeze_at = freeze_at 215 | self.return_idx = return_idx 216 | self.num_stages = num_stages 217 | self.stages = DarkNet_cfg[self.depth][0:num_stages] 218 | 219 | self.conv0 = ConvBNLayer( 220 | ch_in=3, 221 | ch_out=32, 222 | filter_size=3, 223 | stride=1, 224 | padding=1, 225 | norm_type=norm_type, 226 | norm_decay=norm_decay, 227 | freeze_norm=freeze_norm, 228 | data_format=data_format) 229 | 230 | self.downsample0 = DownSample( 231 | ch_in=32, 232 | ch_out=32 * 2, 233 | norm_type=norm_type, 234 | norm_decay=norm_decay, 235 | freeze_norm=freeze_norm, 236 | data_format=data_format) 237 | 238 | self._out_channels = [] 239 | self.darknet_conv_block_list = [] 240 | self.downsample_list = [] 241 | ch_in = [64, 128, 256, 512, 1024] 242 | for i, stage in enumerate(self.stages): 243 | name = 'stage.{}'.format(i) 244 | conv_block = self.add_sublayer( 245 | name, 246 | Blocks( 247 | int(ch_in[i]), 248 | 32 * (2**i), 249 | stage, 250 | norm_type=norm_type, 251 | norm_decay=norm_decay, 252 | freeze_norm=freeze_norm, 253 | data_format=data_format, 254 | name=name)) 255 | self.darknet_conv_block_list.append(conv_block) 256 | if i in return_idx: 257 | self._out_channels.append(64 * (2**i)) 258 | for i in range(num_stages - 1): 259 | down_name = 'stage.{}.downsample'.format(i) 260 | downsample = self.add_sublayer( 261 | down_name, 262 | DownSample( 263 | ch_in=32 * (2**(i + 1)), 264 | ch_out=32 * (2**(i + 2)), 265 | norm_type=norm_type, 266 | norm_decay=norm_decay, 267 | freeze_norm=freeze_norm, 268 | data_format=data_format)) 269 | self.downsample_list.append(downsample) 270 | 271 | def forward(self, inputs): 272 | x = inputs 273 | 274 | out = self.conv0(x) 275 | out = self.downsample0(out) 276 | blocks = [] 277 | for i, conv_block_i in enumerate(self.darknet_conv_block_list): 278 | out = conv_block_i(out) 279 | if i == self.freeze_at: 280 | out.stop_gradient = True 281 | if i in self.return_idx: 282 | blocks.append(out) 283 | if i < self.num_stages - 1: 284 | out = self.downsample_list[i](out) 285 | return blocks 286 | 287 | model = DarkNet() 288 | output = model(paddle.rand((1,3,416,416))) 289 | for out in output: 290 | print(out.shape) 291 | -------------------------------------------------------------------------------- /第10讲 目标检测YOLOv3-backbone/README.md: -------------------------------------------------------------------------------- 1 | ## YOLOv3介绍 2 | 3 | **YOLOv3**是**2018年**提出的目标检测算法,取得了非常优秀的效果。直至今天,**YOLO**系列仍是当前目标检测的**主流算法**,诞生了一系列的变体,如**YOLOv4**,**YOLOv5**,**YOLOX**等。新的算法大多都是对**YOLOv3**算法的**小修改**,本质上没有非常大的改变 4 | 5 | 作为目标检测算法,相比分类算法要复杂一些,**除了需要输出类别以外,还需要对特征物体进行定位**。下面我们一起看一下**YOLOv3**的**具体实现流程**。 6 | 7 |
8 | 9 |
10 | 11 | **YOLOv3**是**anchor based**算法,也就说预先在图片上生成很多的**先验框**,然后我通过神经网络去判断放的**框内有没有我想要的特征物体,如果有特征物体,就对先验框的中心和长和宽进行调整,最终框出物体的长和宽**。 12 | 13 | **YOLOv3**的网络有**三个输出**,对于输入为(3,416,416)的图片,通过这个网络之后会输出(75,13,13),(75,26,26),(75,52,52),分别对应了上面的三张图片,把图像分成了**多个网格**,每个网格上都会放置好**3个先验框**,先验框的**长宽是一开始就固定的**,**13×13的网格用于检测大物体,26×26的网格用于检测中等物体,52×52的网格用于检测小物体**。一共有**13×13×3+26×26×3+52×52×3**个框。**物体中心点落在哪个框内,这个框就负责识别这个物体。** 14 | 15 | **75的含义是3×(num_class+4+1)**,这是对应**voc**数据集,有**20**个类别。**20指的是20个类别的概率**,**4指对先验框的中心点和长宽的修正量,1指这个框内有没有物体。**因为每个格子都有**3个先验框**,所以再乘3。如果使用**coco数据集**,有80个类别,那么对应的维度就是**3×(80+4+1)=255**。 16 | 17 | ## YOLOv3整体网络 18 | 19 |
20 | 21 |
22 | 23 | **YOLOv3**整体网络结构分为了**三大部分**,分别为**backbone**,**neck**和**head**。它的维度变换如下: 24 | 25 |
26 | 27 |
28 | 29 | ## DarkNet53 30 | 31 | **YOLOv3**使用的**backbone**部分为**DarkNet53**,这一部分也成为**特征提取网络**,和我们**分类的网络是基本一致的,这部分是通用的**。也就是说,我们之前讲解过的**Mobilenetv3**也可以放在**YOLOv3**的**backbone**,来实现**检测任务**。 32 | 33 | **DarkNet53**借助于**残差结构**,是一个比较**深层**的网络,一共有**53**层网络。总体的形式如下: 34 | 35 |
36 | 37 |
38 | 39 | 通过学习**Mobilenetv3**,相比大家学习新的网络就轻松很多了。**DarkNet53**从图上看,就是**Residual Block的多次使用**,我们只要清楚了**Residual Block**的结构,整个**DarkNet53**就非常清晰了。 40 | 41 | Residual Block是由**两个卷积归一化层**组成,先用一个**1×1**的卷积层,把通道数**降低为一半**,再用一个**3×3**的卷积层提取特征,最后再将**输入和输出相加得到最后的输出**。**DarkNet53**分别使用了[1,2,8,8,4]次**Residual Block**,最后三次的block分别输出了**三个特征图**,shape分别为(52,52,256),(26,26,512),(13,13,1024),再将这个三个输出输入的**neck**部分中。 42 | 43 | 和分类网络相比,无非是**少了尾部的结构**,换句话说加上尾部结构,**DarkNet53**也可以用于**分类任务**,当然**Mobilenetv3去掉尾部结构,也可以适用于检测任务。** 44 | 45 | ## Paddle代码实现 46 | 47 | ``` 48 | import paddle 49 | import paddle.nn as nn 50 | import paddle.nn.functional as F 51 | from paddle import ParamAttr 52 | from paddle.regularizer import L2Decay 53 | 54 | def batch_norm(ch, 55 | norm_type='bn', 56 | norm_decay=0., 57 | freeze_norm=False, 58 | initializer=None, 59 | data_format='NCHW'): 60 | if norm_type == 'sync_bn': 61 | batch_norm = nn.SyncBatchNorm 62 | else: 63 | batch_norm = nn.BatchNorm2D 64 | 65 | norm_lr = 0. if freeze_norm else 1. 66 | weight_attr = ParamAttr( 67 | initializer=initializer, 68 | learning_rate=norm_lr, 69 | regularizer=L2Decay(norm_decay), 70 | trainable=False if freeze_norm else True) 71 | bias_attr = ParamAttr( 72 | learning_rate=norm_lr, 73 | regularizer=L2Decay(norm_decay), 74 | trainable=False if freeze_norm else True) 75 | 76 | norm_layer = batch_norm( 77 | ch, 78 | weight_attr=weight_attr, 79 | bias_attr=bias_attr, 80 | data_format=data_format) 81 | 82 | norm_params = norm_layer.parameters() 83 | if freeze_norm: 84 | for param in norm_params: 85 | param.stop_gradient = True 86 | 87 | return norm_layer 88 | 89 | 90 | class ConvBNLayer(nn.Layer): 91 | def __init__(self, 92 | ch_in, 93 | ch_out, 94 | filter_size=3, 95 | stride=1, 96 | groups=1, 97 | padding=0, 98 | norm_type='bn', 99 | norm_decay=0., 100 | act="leaky", 101 | freeze_norm=False, 102 | data_format='NCHW', 103 | name=''): 104 | 105 | super(ConvBNLayer, self).__init__() 106 | 107 | self.conv = nn.Conv2D( 108 | in_channels=ch_in, 109 | out_channels=ch_out, 110 | kernel_size=filter_size, 111 | stride=stride, 112 | padding=padding, 113 | groups=groups, 114 | data_format=data_format, 115 | bias_attr=False) 116 | self.batch_norm = batch_norm( 117 | ch_out, 118 | norm_type=norm_type, 119 | norm_decay=norm_decay, 120 | freeze_norm=freeze_norm, 121 | data_format=data_format) 122 | self.act = act 123 | 124 | def forward(self, inputs): 125 | out = self.conv(inputs) 126 | out = self.batch_norm(out) 127 | if self.act == 'leaky': 128 | out = F.leaky_relu(out, 0.1) 129 | return out 130 | 131 | 132 | class DownSample(nn.Layer): 133 | def __init__(self, 134 | ch_in, 135 | ch_out, 136 | filter_size=3, 137 | stride=2, 138 | padding=1, 139 | norm_type='bn', 140 | norm_decay=0., 141 | freeze_norm=False, 142 | data_format='NCHW'): 143 | 144 | super(DownSample, self).__init__() 145 | 146 | self.conv_bn_layer = ConvBNLayer( 147 | ch_in=ch_in, 148 | ch_out=ch_out, 149 | filter_size=filter_size, 150 | stride=stride, 151 | padding=padding, 152 | norm_type=norm_type, 153 | norm_decay=norm_decay, 154 | freeze_norm=freeze_norm, 155 | data_format=data_format) 156 | self.ch_out = ch_out 157 | 158 | def forward(self, inputs): 159 | out = self.conv_bn_layer(inputs) 160 | return out 161 | 162 | 163 | class BasicBlock(nn.Layer): 164 | def __init__(self, 165 | ch_in, 166 | ch_out, 167 | norm_type='bn', 168 | norm_decay=0., 169 | freeze_norm=False, 170 | data_format='NCHW'): 171 | 172 | super(BasicBlock, self).__init__() 173 | 174 | self.conv1 = ConvBNLayer( 175 | ch_in=ch_in, 176 | ch_out=ch_out, 177 | filter_size=1, 178 | stride=1, 179 | padding=0, 180 | norm_type=norm_type, 181 | norm_decay=norm_decay, 182 | freeze_norm=freeze_norm, 183 | data_format=data_format) 184 | self.conv2 = ConvBNLayer( 185 | ch_in=ch_out, 186 | ch_out=ch_out * 2, 187 | filter_size=3, 188 | stride=1, 189 | padding=1, 190 | norm_type=norm_type, 191 | norm_decay=norm_decay, 192 | freeze_norm=freeze_norm, 193 | data_format=data_format) 194 | 195 | def forward(self, inputs): 196 | conv1 = self.conv1(inputs) 197 | conv2 = self.conv2(conv1) 198 | out = paddle.add(x=inputs, y=conv2) 199 | return out 200 | 201 | 202 | class Blocks(nn.Layer): 203 | def __init__(self, 204 | ch_in, 205 | ch_out, 206 | count, 207 | norm_type='bn', 208 | norm_decay=0., 209 | freeze_norm=False, 210 | name=None, 211 | data_format='NCHW'): 212 | 213 | super(Blocks, self).__init__() 214 | 215 | self.basicblock0 = BasicBlock( 216 | ch_in, 217 | ch_out, 218 | norm_type=norm_type, 219 | norm_decay=norm_decay, 220 | freeze_norm=freeze_norm, 221 | data_format=data_format) 222 | self.res_out_list = [] 223 | for i in range(1, count): 224 | block_name = '{}.{}'.format(name, i) 225 | res_out = self.add_sublayer( 226 | block_name, 227 | BasicBlock( 228 | ch_out * 2, 229 | ch_out, 230 | norm_type=norm_type, 231 | norm_decay=norm_decay, 232 | freeze_norm=freeze_norm, 233 | data_format=data_format)) 234 | self.res_out_list.append(res_out) 235 | self.ch_out = ch_out 236 | 237 | def forward(self, inputs): 238 | y = self.basicblock0(inputs) 239 | for basic_block_i in self.res_out_list: 240 | y = basic_block_i(y) 241 | return y 242 | 243 | 244 | DarkNet_cfg = {53: ([1, 2, 8, 8, 4])} 245 | 246 | class DarkNet(nn.Layer): 247 | __shared__ = ['norm_type', 'data_format'] 248 | 249 | def __init__(self, 250 | depth=53, 251 | freeze_at=-1, 252 | return_idx=[2, 3, 4], 253 | num_stages=5, 254 | norm_type='bn', 255 | norm_decay=0., 256 | freeze_norm=False, 257 | data_format='NCHW'): 258 | 259 | super(DarkNet, self).__init__() 260 | self.depth = depth 261 | self.freeze_at = freeze_at 262 | self.return_idx = return_idx 263 | self.num_stages = num_stages 264 | self.stages = DarkNet_cfg[self.depth][0:num_stages] 265 | 266 | self.conv0 = ConvBNLayer( 267 | ch_in=3, 268 | ch_out=32, 269 | filter_size=3, 270 | stride=1, 271 | padding=1, 272 | norm_type=norm_type, 273 | norm_decay=norm_decay, 274 | freeze_norm=freeze_norm, 275 | data_format=data_format) 276 | 277 | self.downsample0 = DownSample( 278 | ch_in=32, 279 | ch_out=32 * 2, 280 | norm_type=norm_type, 281 | norm_decay=norm_decay, 282 | freeze_norm=freeze_norm, 283 | data_format=data_format) 284 | 285 | self._out_channels = [] 286 | self.darknet_conv_block_list = [] 287 | self.downsample_list = [] 288 | ch_in = [64, 128, 256, 512, 1024] 289 | for i, stage in enumerate(self.stages): 290 | name = 'stage.{}'.format(i) 291 | conv_block = self.add_sublayer( 292 | name, 293 | Blocks( 294 | int(ch_in[i]), 295 | 32 * (2**i), 296 | stage, 297 | norm_type=norm_type, 298 | norm_decay=norm_decay, 299 | freeze_norm=freeze_norm, 300 | data_format=data_format, 301 | name=name)) 302 | self.darknet_conv_block_list.append(conv_block) 303 | if i in return_idx: 304 | self._out_channels.append(64 * (2**i)) 305 | for i in range(num_stages - 1): 306 | down_name = 'stage.{}.downsample'.format(i) 307 | downsample = self.add_sublayer( 308 | down_name, 309 | DownSample( 310 | ch_in=32 * (2**(i + 1)), 311 | ch_out=32 * (2**(i + 2)), 312 | norm_type=norm_type, 313 | norm_decay=norm_decay, 314 | freeze_norm=freeze_norm, 315 | data_format=data_format)) 316 | self.downsample_list.append(downsample) 317 | 318 | def forward(self, inputs): 319 | x = inputs 320 | 321 | out = self.conv0(x) 322 | out = self.downsample0(out) 323 | blocks = [] 324 | for i, conv_block_i in enumerate(self.darknet_conv_block_list): 325 | out = conv_block_i(out) 326 | if i == self.freeze_at: 327 | out.stop_gradient = True 328 | if i in self.return_idx: 329 | blocks.append(out) 330 | if i < self.num_stages - 1: 331 | out = self.downsample_list[i](out) 332 | return blocks 333 | 334 | model = DarkNet() 335 | output = model(paddle.rand((1,3,416,416))) 336 | for out in output: 337 | print(out.shape) 338 | 339 | ``` 340 | 341 | ## 参考资料 342 | 343 | https://blog.csdn.net/weixin_44791964/article/details/105310627 344 | 345 | https://blog.csdn.net/qq_40210586/article/details/106144197 346 | --------------------------------------------------------------------------------