├── .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 |
58 |
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 |
--------------------------------------------------------------------------------