apcheCN 单独建立库
@@ -1,110 +0,0 @@
|
||||
# ApacheCN 深度学习译文集
|
||||
|
||||
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
>
|
||||
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
|
||||
>
|
||||
> 不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则 2.3.c
|
||||
|
||||
* [在线阅读](https://dl.apachecn.org)
|
||||
* [在线阅读(Gitee)](https://apachecn.gitee.io/apachecn-dl-zh/)
|
||||
* [ApacheCN 面试求职交流群 724187166](https://jq.qq.com/?_wv=1027&k=54ujcL3)
|
||||
* [ApacheCN 学习资源](http://www.apachecn.org/)
|
||||
|
||||
## 目录
|
||||
|
||||
+ [Sklearn 与 TensorFlow 机器学习实用指南第二版](docs/hands-on-ml-2e-zh/SUMMARY.md)
|
||||
+ [PyTorch 自然语言处理](docs/nlp-pytorch-zh/SUMMARY.md)
|
||||
+ [TensorFlow 1.x 深度学习秘籍](docs/tf-1x-dl-cookbook/SUMMARY.md)
|
||||
+ [PyTorch 中文官方教程 1.7](docs/pt-tut-17/SUMMARY.md)
|
||||
+ [使用 TensorFlow 构建机器学习项目中文版](docs/build-ml-proj-tf-zh/SUMMARY.md)
|
||||
+ [TensorFlow 深度学习中文第二版](docs/dl-tf-2e-zh/SUMMARY.md)
|
||||
+ [TensorFlow 深度学习实战指南中文版](docs/hands-on-dl-tf-zh/SUMMARY.md)
|
||||
+ [精通 TensorFlow 1.x](docs/mastering-tf-1x-zh/SUMMARY.md)
|
||||
+ [TensorFlow 机器学习秘籍中文第二版](docs/tf-ml-cookbook-2e-zh/SUMMARY.md)
|
||||
+ [与 TensorFlow 的初次接触](docs/first_contact_with_tensorFlow/SUMMARY.md)
|
||||
+ [TensorFlow 学习指南](docs/learning-tf-zh/SUMMARY.md)
|
||||
+ [TensorFlow Rager 教程](docs/tf-eager-tut/SUMMARY.md)
|
||||
+ [TensorFlow 高效编程](docs/effective-tf.md)
|
||||
+ [图嵌入综述:问题,技术与应用](docs/ge-survey-arxiv-1709-07604-zh/SUMMARY.md)
|
||||
+ [基于深度学习的推荐系统:综述和新视角](docs/rs-survey-arxiv-1707-07435-zh/SUMMARY.md)
|
||||
+ [关于卷积神经网络我们理解了什么](docs/what-do-we-understand-about-convnet/SUMMARY.md)
|
||||
+ [机器学习超级复习笔记](docs/super-machine-learning-revision-notes/SUMMARY.md)
|
||||
+ [Python 迁移学习实用指南](docs/handson-tl-py/SUMMARY.md)
|
||||
+ [面向计算机视觉的深度学习](docs/dl-cv/SUMMARY.md)
|
||||
+ [深度学习快速参考](docs/dl-quick-ref/SUMMARY.md)
|
||||
+ [TensorFlow 2.0 快速入门指南](docs/tf-20-quick-start-guide/SUMMARY.md)
|
||||
+ [TensorFlow 入门](docs/get-start-tf/SUMMARY.md)
|
||||
+ [TensorFlow 卷积神经网络实用指南](docs/handson-cnn-tf/SUMMARY.md)
|
||||
+ [Python 人工智能中文版](docs/ai-py/SUMMARY.md)
|
||||
+ [Python 无监督学习实用指南](docs/handson-unsup-learn-py/SUMMARY.md)
|
||||
+ [生成对抗网络项目](docs/gan-proj/SUMMARY.md)
|
||||
+ [TensorFlow 智能移动项目](docs/intel-mobi-proj-tf/SUMMARY.md)
|
||||
+ [TensorFlow 和 Keras 应用开发入门](docs/begin-app-dev-tf-keras/SUMMARY.md)
|
||||
+ [TensorFlow 图像深度学习实用指南](docs/handson-dl-img-tf/SUMMARY.md)
|
||||
+ [Python 元学习实用指南](docs/handson-meta-learn-py/SUMMARY.md)
|
||||
+ [Python 强化学习实用指南](docs/handson-rl-py/SUMMARY.md)
|
||||
+ [Python 智能项目](docs/intel-proj-py/SUMMARY.md)
|
||||
+ [精通 Sklearn 和 TensorFlow 预测性分析](docs/master-pred-anal-sklearn-tf/SUMMARY.md)
|
||||
+ [TensorFlow 2.0 的新增功能](docs/whats-new-tf2/SUMMARY.md)
|
||||
+ [UCB CS294-112 深度强化学习中文笔记](docs/ucb-cs294-112-notes-zh/SUMMARY.md)
|
||||
+ [TensorFlow 2 和 Keras 高级深度学习](docs/adv-dl-tf2-keras/SUMMARY.md)
|
||||
+ [GCP 上的人工智能实用指南](docs/handson-ai-gcp/SUMMARY.md)
|
||||
+ [Python 深度学习架构实用指南](docs/handson-dl-arch-py/SUMMARY.md)
|
||||
+ [Python Web 深度学习实用指南](docs/handson-py-dl-web/SUMMARY.md)
|
||||
+ [精通 TensorFlow 2.x 计算机视觉](docs/master-cv-tf-2x/SUMMARY.md)
|
||||
+ [TensorFlow Lite,ML Kit 和 Flutter 移动深度学习](docs/mobi-dl-tflite/SUMMARY.md)
|
||||
+ [PyTorch 人工智能研讨会](docs/dl-pt-workshop/SUMMARY.md)
|
||||
+ [Python 一次学习实用指南](docs/handson-1shot-learn-py/SUMMARY.md)
|
||||
+ [Python 自然语言处理实用指南](docs/handson-nlp-pt-1x/SUMMARY.md)
|
||||
+ [PyTorch 人工智能基础知识](docs/pt-ai-fund/SUMMARY.md)
|
||||
+ [PyTorch 深度学习实用指南](docs/pt-dl-handson/SUMMARY.md)
|
||||
+ [TensorFlow 强化学习](docs/rl-tf/SUMMARY.md)
|
||||
|
||||
## 下载
|
||||
|
||||
### Docker
|
||||
|
||||
```
|
||||
docker pull apachecn0/apachecn-dl-zh
|
||||
docker run -tid -p <port>:80 apachecn0/apachecn-dl-zh
|
||||
# 访问 http://localhost:{port} 查看文档
|
||||
```
|
||||
|
||||
### PYPI
|
||||
|
||||
```
|
||||
pip install apachecn-dl-zh
|
||||
apachecn-dl-zh <port>
|
||||
# 访问 http://localhost:{port} 查看文档
|
||||
```
|
||||
|
||||
### NPM
|
||||
|
||||
```
|
||||
npm install -g apachecn-dl-zh
|
||||
apachecn-dl-zh <port>
|
||||
# 访问 http://localhost:{port} 查看文档
|
||||
```
|
||||
|
||||
## 贡献指南
|
||||
|
||||
本项目需要校对,欢迎大家提交 Pull Request。
|
||||
|
||||
> 请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)
|
||||
|
||||
## 联系方式
|
||||
|
||||
### 负责人
|
||||
|
||||
* [飞龙](https://github.com/wizardforcel): 562826179
|
||||
|
||||
### 其他
|
||||
|
||||
* 在我们的 [apachecn/apachecn-tf-zh](https://github.com/apachecn/apachecn-tf-zh) github 上提 issue.
|
||||
* 发邮件到 Email: `apachecn@163.com`.
|
||||
* 在我们的 [组织学习交流群](http://www.apachecn.org/organization/348.html) 中联系群主/管理员即可.
|
||||
|
||||
## 赞助我们
|
||||
|
||||

|
||||
@@ -1,613 +0,0 @@
|
||||
+ [Sklearn 与 TensorFlow 机器学习实用指南第二版](docs/hands-on-ml-2e-zh/README.md)
|
||||
+ [零、前言](docs/hands-on-ml-2e-zh/0.md)
|
||||
+ [一、机器学习概览](docs/hands-on-ml-2e-zh/1.md)
|
||||
+ [二、端到端的机器学习项目](docs/hands-on-ml-2e-zh/2.md)
|
||||
+ [三、分类](docs/hands-on-ml-2e-zh/3.md)
|
||||
+ [四、训练模型](docs/hands-on-ml-2e-zh/4.md)
|
||||
+ [五、支持向量机](docs/hands-on-ml-2e-zh/5.md)
|
||||
+ [六、决策树](docs/hands-on-ml-2e-zh/6.md)
|
||||
+ [七、集成学习和随机森林](docs/hands-on-ml-2e-zh/7.md)
|
||||
+ [八、降维](docs/hands-on-ml-2e-zh/8.md)
|
||||
+ [十、使用 Keras 搭建人工神经网络](docs/hands-on-ml-2e-zh/10.md)
|
||||
+ [十一、训练深度神经网络](docs/hands-on-ml-2e-zh/11.md)
|
||||
+ [十二、使用 TensorFlow 自定义模型并训练](docs/hands-on-ml-2e-zh/12.md)
|
||||
+ [十三、使用 TensorFlow 加载和预处理数据](docs/hands-on-ml-2e-zh/13.md)
|
||||
+ [十四、使用卷积神经网络实现深度计算机视觉](docs/hands-on-ml-2e-zh/14.md)
|
||||
+ [十五、使用 RNN 和 CNN 处理序列](docs/hands-on-ml-2e-zh/15.md)
|
||||
+ [十六、使用 RNN 和注意力机制进行自然语言处理](docs/hands-on-ml-2e-zh/16.md)
|
||||
+ [十七、使用自编码器和 GAN 做表征学习和生成式学习](docs/hands-on-ml-2e-zh/17.md)
|
||||
+ [十八、强化学习](docs/hands-on-ml-2e-zh/18.md)
|
||||
+ [十九、规模化训练和部署 TensorFlow 模型](docs/hands-on-ml-2e-zh/19.md)
|
||||
+ [PyTorch 自然语言处理](docs/nlp-pytorch-zh/README.md)
|
||||
+ [一、基础介绍](docs/nlp-pytorch-zh/1.md)
|
||||
+ [二、传统 NLP 快速回顾](docs/nlp-pytorch-zh/2.md)
|
||||
+ [三、神经网络基础组件](docs/nlp-pytorch-zh/3.md)
|
||||
+ [四、自然语言处理的前馈网络](docs/nlp-pytorch-zh/4.md)
|
||||
+ [五、嵌入单词和类型](docs/nlp-pytorch-zh/5.md)
|
||||
+ [六、自然语言处理的序列模型](docs/nlp-pytorch-zh/6.md)
|
||||
+ [七、自然语言处理的进阶序列模型](docs/nlp-pytorch-zh/7.md)
|
||||
+ [八、自然语言处理的高级序列模型](docs/nlp-pytorch-zh/8.md)
|
||||
+ [九、经典, 前沿和后续步骤](docs/nlp-pytorch-zh/9.md)
|
||||
+ [TensorFlow 1.x 深度学习秘籍](docs/tf-1x-dl-cookbook/README.md)
|
||||
+ [零、前言](docs/tf-1x-dl-cookbook/00.md)
|
||||
+ [一、TensorFlow 简介](docs/tf-1x-dl-cookbook/01.md)
|
||||
+ [二、回归](docs/tf-1x-dl-cookbook/02.md)
|
||||
+ [三、神经网络:感知器](docs/tf-1x-dl-cookbook/03.md)
|
||||
+ [四、卷积神经网络](docs/tf-1x-dl-cookbook/04.md)
|
||||
+ [五、高级卷积神经网络](docs/tf-1x-dl-cookbook/05.md)
|
||||
+ [六、循环神经网络](docs/tf-1x-dl-cookbook/06.md)
|
||||
+ [七、无监督学习](docs/tf-1x-dl-cookbook/07.md)
|
||||
+ [八、自编码器](docs/tf-1x-dl-cookbook/08.md)
|
||||
+ [九、强化学习](docs/tf-1x-dl-cookbook/09.md)
|
||||
+ [十、移动计算](docs/tf-1x-dl-cookbook/10.md)
|
||||
+ [十一、生成模型和 CapsNet](docs/tf-1x-dl-cookbook/11.md)
|
||||
+ [十二、分布式 TensorFlow 和云深度学习](docs/tf-1x-dl-cookbook/12.md)
|
||||
+ [十三、AutoML 和学习如何学习(元学习)](docs/tf-1x-dl-cookbook/13.md)
|
||||
+ [十四、TensorFlow 处理单元](docs/tf-1x-dl-cookbook/14.md)
|
||||
+ [PyTorch 中文官方教程 1.7](docs/pt-tut-17/README.md)
|
||||
+ [学习 PyTorch](docs/pt-tut-17/01.md)
|
||||
+ [PyTorch 深度学习:60 分钟的突击](docs/pt-tut-17/02.md)
|
||||
+ [张量](docs/pt-tut-17/03.md)
|
||||
+ [`torch.autograd`的简要介绍](docs/pt-tut-17/04.md)
|
||||
+ [神经网络](docs/pt-tut-17/05.md)
|
||||
+ [训练分类器](docs/pt-tut-17/06.md)
|
||||
+ [通过示例学习 PyTorch](docs/pt-tut-17/07.md)
|
||||
+ [热身:NumPy](docs/pt-tut-17/08.md)
|
||||
+ [PyTorch:张量](docs/pt-tut-17/09.md)
|
||||
+ [PyTorch:张量和 Autograd](docs/pt-tut-17/10.md)
|
||||
+ [PyTorch:定义新的 Autograd 函数](docs/pt-tut-17/11.md)
|
||||
+ [PyTorch:`nn`](docs/pt-tut-17/12.md)
|
||||
+ [PyTorch:`optim`](docs/pt-tut-17/13.md)
|
||||
+ [PyTorch:自定义`nn`模块](docs/pt-tut-17/14.md)
|
||||
+ [PyTorch:控制流 + 权重共享](docs/pt-tut-17/15.md)
|
||||
+ [`torch.nn`到底是什么?](docs/pt-tut-17/16.md)
|
||||
+ [使用 TensorBoard 可视化模型,数据和训练](docs/pt-tut-17/17.md)
|
||||
+ [图片/视频](docs/pt-tut-17/18.md)
|
||||
+ [`torchvision`对象检测微调教程](docs/pt-tut-17/19.md)
|
||||
+ [计算机视觉的迁移学习教程](docs/pt-tut-17/20.md)
|
||||
+ [对抗示例生成](docs/pt-tut-17/21.md)
|
||||
+ [DCGAN 教程](docs/pt-tut-17/22.md)
|
||||
+ [音频](docs/pt-tut-17/23.md)
|
||||
+ [音频 I/O 和`torchaudio`的预处理](docs/pt-tut-17/24.md)
|
||||
+ [使用`torchaudio`的语音命令识别](docs/pt-tut-17/25.md)
|
||||
+ [文本](docs/pt-tut-17/26.md)
|
||||
+ [使用`nn.Transformer`和`torchtext`的序列到序列建模](docs/pt-tut-17/27.md)
|
||||
+ [从零开始的 NLP:使用字符级 RNN 分类名称](docs/pt-tut-17/28.md)
|
||||
+ [从零开始的 NLP:使用字符级 RNN 生成名称](docs/pt-tut-17/29.md)
|
||||
+ [从零开始的 NLP:使用序列到序列网络和注意力的翻译](docs/pt-tut-17/30.md)
|
||||
+ [使用`torchtext`的文本分类](docs/pt-tut-17/31.md)
|
||||
+ [`torchtext`语言翻译](docs/pt-tut-17/32.md)
|
||||
+ [强化学习](docs/pt-tut-17/33.md)
|
||||
+ [强化学习(DQN)教程](docs/pt-tut-17/34.md)
|
||||
+ [训练玩马里奥的 RL 智能体](docs/pt-tut-17/35.md)
|
||||
+ [在生产中部署 PyTorch 模型](docs/pt-tut-17/36.md)
|
||||
+ [通过使用 Flask 的 REST API 在 Python 中部署 PyTorch](docs/pt-tut-17/37.md)
|
||||
+ [TorchScript 简介](docs/pt-tut-17/38.md)
|
||||
+ [在 C++ 中加载 TorchScript 模型](docs/pt-tut-17/39.md)
|
||||
+ [将模型从 PyTorch 导出到 ONNX 并使用 ONNX 运行时运行它(可选)](docs/pt-tut-17/40.md)
|
||||
+ [前端 API](docs/pt-tut-17/41.md)
|
||||
+ [PyTorch 中的命名张量简介(原型)](docs/pt-tut-17/42.md)
|
||||
+ [PyTorch 中通道在最后的内存格式(beta)](docs/pt-tut-17/43.md)
|
||||
+ [使用 PyTorch C++ 前端](docs/pt-tut-17/44.md)
|
||||
+ [自定义 C++ 和 CUDA 扩展](docs/pt-tut-17/45.md)
|
||||
+ [使用自定义 C++ 运算符扩展 TorchScript](docs/pt-tut-17/46.md)
|
||||
+ [使用自定义 C++ 类扩展 TorchScript](docs/pt-tut-17/47.md)
|
||||
+ [TorchScript 中的动态并行性](docs/pt-tut-17/48.md)
|
||||
+ [C++ 前端中的 Autograd](docs/pt-tut-17/49.md)
|
||||
+ [在 C++ 中注册调度运算符](docs/pt-tut-17/50.md)
|
||||
+ [模型优化](docs/pt-tut-17/51.md)
|
||||
+ [分析您的 PyTorch 模块](docs/pt-tut-17/52.md)
|
||||
+ [使用 Ray Tune 的超参数调整](docs/pt-tut-17/53.md)
|
||||
+ [模型剪裁教程](docs/pt-tut-17/54.md)
|
||||
+ [LSTM 单词语言模型上的动态量化(beta)](docs/pt-tut-17/55.md)
|
||||
+ [BERT 上的动态量化(Beta)](docs/pt-tut-17/56.md)
|
||||
+ [PyTorch 中使用 Eager 模式的静态量化(beta)](docs/pt-tut-17/57.md)
|
||||
+ [计算机视觉的量化迁移学习教程(beta)](docs/pt-tut-17/58.md)
|
||||
+ [并行和分布式训练](docs/pt-tut-17/59.md)
|
||||
+ [PyTorch 分布式概述](docs/pt-tut-17/60.md)
|
||||
+ [单机模型并行最佳实践](docs/pt-tut-17/61.md)
|
||||
+ [分布式数据并行入门](docs/pt-tut-17/62.md)
|
||||
+ [用 PyTorch 编写分布式应用](docs/pt-tut-17/63.md)
|
||||
+ [分布式 RPC 框架入门](docs/pt-tut-17/64.md)
|
||||
+ [使用分布式 RPC 框架实现参数服务器](docs/pt-tut-17/65.md)
|
||||
+ [使用 RPC 的分布式管道并行化](docs/pt-tut-17/66.md)
|
||||
+ [使用异步执行实现批量 RPC 处理](docs/pt-tut-17/67.md)
|
||||
+ [将分布式`DataParallel`与分布式 RPC 框架相结合](docs/pt-tut-17/68.md)
|
||||
+ [使用 TensorFlow 构建机器学习项目中文版](docs/build-ml-proj-tf-zh/README.md)
|
||||
+ [一、探索和转换数据](docs/build-ml-proj-tf-zh/ch01.md)
|
||||
+ [二、聚类](docs/build-ml-proj-tf-zh/ch02.md)
|
||||
+ [三、线性回归](docs/build-ml-proj-tf-zh/ch03.md)
|
||||
+ [四、逻辑回归](docs/build-ml-proj-tf-zh/ch04.md)
|
||||
+ [五、简单的前馈神经网络](docs/build-ml-proj-tf-zh/ch05.md)
|
||||
+ [六、卷积神经网络](docs/build-ml-proj-tf-zh/ch06.md)
|
||||
+ [七、循环神经网络和 LSTM](docs/build-ml-proj-tf-zh/ch07.md)
|
||||
+ [八、深度神经网络](docs/build-ml-proj-tf-zh/ch08.md)
|
||||
+ [九、大规模运行模型 -- GPU 和服务](docs/build-ml-proj-tf-zh/ch09.md)
|
||||
+ [十、库安装和其他提示](docs/build-ml-proj-tf-zh/ch10.md)
|
||||
+ [TensorFlow 深度学习中文第二版](docs/dl-tf-2e-zh/README.md)
|
||||
+ [一、人工神经网络](docs/dl-tf-2e-zh/ch01.md)
|
||||
+ [二、TensorFlow v1.6 的新功能是什么?](docs/dl-tf-2e-zh/ch02.md)
|
||||
+ [三、实现前馈神经网络](docs/dl-tf-2e-zh/ch03.md)
|
||||
+ [四、CNN 实战](docs/dl-tf-2e-zh/ch04.md)
|
||||
+ [五、使用 TensorFlow 实现自编码器](docs/dl-tf-2e-zh/ch05.md)
|
||||
+ [六、RNN 和梯度消失或爆炸问题](docs/dl-tf-2e-zh/ch06.md)
|
||||
+ [七、TensorFlow GPU 配置](docs/dl-tf-2e-zh/ch07.md)
|
||||
+ [八、TFLearn](docs/dl-tf-2e-zh/ch08.md)
|
||||
+ [九、使用协同过滤的电影推荐](docs/dl-tf-2e-zh/ch09.md)
|
||||
+ [十、OpenAI Gym](docs/dl-tf-2e-zh/ch10.md)
|
||||
+ [TensorFlow 深度学习实战指南中文版](docs/hands-on-dl-tf-zh/README.md)
|
||||
+ [一、入门](docs/hands-on-dl-tf-zh/ch01.md)
|
||||
+ [二、深度神经网络](docs/hands-on-dl-tf-zh/ch02.md)
|
||||
+ [三、卷积神经网络](docs/hands-on-dl-tf-zh/ch03.md)
|
||||
+ [四、循环神经网络介绍](docs/hands-on-dl-tf-zh/ch04.md)
|
||||
+ [五、总结](docs/hands-on-dl-tf-zh/ch05.md)
|
||||
+ [精通 TensorFlow 1.x](docs/mastering-tf-1x-zh/README.md)
|
||||
+ [一、TensorFlow 101](docs/mastering-tf-1x-zh/ch01.md)
|
||||
+ [二、TensorFlow 的高级库](docs/mastering-tf-1x-zh/ch02.md)
|
||||
+ [三、Keras 101](docs/mastering-tf-1x-zh/ch03.md)
|
||||
+ [四、TensorFlow 中的经典机器学习](docs/mastering-tf-1x-zh/ch04.md)
|
||||
+ [五、TensorFlow 和 Keras 中的神经网络和 MLP](docs/mastering-tf-1x-zh/ch05.md)
|
||||
+ [六、TensorFlow 和 Keras 中的 RNN](docs/mastering-tf-1x-zh/ch06.md)
|
||||
+ [七、TensorFlow 和 Keras 中的用于时间序列数据的 RNN](docs/mastering-tf-1x-zh/ch07.md)
|
||||
+ [八、TensorFlow 和 Keras 中的用于文本数据的 RNN](docs/mastering-tf-1x-zh/ch08.md)
|
||||
+ [九、TensorFlow 和 Keras 中的 CNN](docs/mastering-tf-1x-zh/ch09.md)
|
||||
+ [十、TensorFlow 和 Keras 中的自编码器](docs/mastering-tf-1x-zh/ch10.md)
|
||||
+ [十一、TF 服务:生产中的 TensorFlow 模型](docs/mastering-tf-1x-zh/ch11.md)
|
||||
+ [十二、迁移学习和预训练模型](docs/mastering-tf-1x-zh/ch12.md)
|
||||
+ [十三、深度强化学习](docs/mastering-tf-1x-zh/ch13.md)
|
||||
+ [十四、生成对抗网络](docs/mastering-tf-1x-zh/ch14.md)
|
||||
+ [十五、TensorFlow 集群的分布式模型](docs/mastering-tf-1x-zh/ch15.md)
|
||||
+ [十六、移动和嵌入式平台上的 TensorFlow 模型](docs/mastering-tf-1x-zh/ch16.md)
|
||||
+ [十七、R 中的 TensorFlow 和 Keras](docs/mastering-tf-1x-zh/ch17.md)
|
||||
+ [十八、调试 TensorFlow 模型](docs/mastering-tf-1x-zh/ch18.md)
|
||||
+ [十九、张量处理单元](docs/mastering-tf-1x-zh/ch19.md)
|
||||
+ [TensorFlow 机器学习秘籍中文第二版](docs/tf-ml-cookbook-2e-zh/README.md)
|
||||
+ [一、TensorFlow 入门](docs/tf-ml-cookbook-2e-zh/ch01.md)
|
||||
+ [二、TensorFlow 的方式](docs/tf-ml-cookbook-2e-zh/ch02.md)
|
||||
+ [三、线性回归](docs/tf-ml-cookbook-2e-zh/ch03.md)
|
||||
+ [四、支持向量机](docs/tf-ml-cookbook-2e-zh/ch04.md)
|
||||
+ [五、最近邻方法](docs/tf-ml-cookbook-2e-zh/ch05.md)
|
||||
+ [六、神经网络](docs/tf-ml-cookbook-2e-zh/ch06.md)
|
||||
+ [七、自然语言处理](docs/tf-ml-cookbook-2e-zh/ch07.md)
|
||||
+ [八、卷积神经网络](docs/tf-ml-cookbook-2e-zh/ch08.md)
|
||||
+ [九、循环神经网络](docs/tf-ml-cookbook-2e-zh/ch09.md)
|
||||
+ [十、将 TensorFlow 投入生产](docs/tf-ml-cookbook-2e-zh/ch10.md)
|
||||
+ [十一、更多 TensorFlow](docs/tf-ml-cookbook-2e-zh/ch11.md)
|
||||
+ [与 TensorFlow 的初次接触](docs/first_contact_with_tensorFlow/README.md)
|
||||
+ [前言](docs/first_contact_with_tensorFlow/0.md)
|
||||
+ [1. TensorFlow 基础知识](docs/first_contact_with_tensorFlow/1.md)
|
||||
+ [2. TensorFlow 中的线性回归](docs/first_contact_with_tensorFlow/2.md)
|
||||
+ [3. TensorFlow 中的聚类](docs/first_contact_with_tensorFlow/3.md)
|
||||
+ [4. TensorFlow 中的单层神经网络](docs/first_contact_with_tensorFlow/4.md)
|
||||
+ [5. TensorFlow 中的多层神经网络](docs/first_contact_with_tensorFlow/5.md)
|
||||
+ [6. 并行](docs/first_contact_with_tensorFlow/6.md)
|
||||
+ [后记](docs/first_contact_with_tensorFlow/7.md)
|
||||
+ [TensorFlow 学习指南](docs/learning-tf-zh/README.md)
|
||||
+ [一、基础](docs/learning-tf-zh/1.md)
|
||||
+ [二、线性模型](docs/learning-tf-zh/2.md)
|
||||
+ [三、学习](docs/learning-tf-zh/3.md)
|
||||
+ [四、分布式](docs/learning-tf-zh/4.md)
|
||||
+ [TensorFlow Rager 教程](docs/tf-eager-tut/README.md)
|
||||
+ [一、如何使用 TensorFlow Eager 构建简单的神经网络](docs/tf-eager-tut/1.md)
|
||||
+ [二、在 Eager 模式中使用指标](docs/tf-eager-tut/2.md)
|
||||
+ [三、如何保存和恢复训练模型](docs/tf-eager-tut/3.md)
|
||||
+ [四、文本序列到 TFRecords](docs/tf-eager-tut/4.md)
|
||||
+ [五、如何将原始图片数据转换为 TFRecords](docs/tf-eager-tut/5.md)
|
||||
+ [六、如何使用 TensorFlow Eager 从 TFRecords 批量读取数据](docs/tf-eager-tut/6.md)
|
||||
+ [七、使用 TensorFlow Eager 构建用于情感识别的卷积神经网络(CNN)](docs/tf-eager-tut/7.md)
|
||||
+ [八、用于 TensorFlow Eager 序列分类的动态循坏神经网络](docs/tf-eager-tut/8.md)
|
||||
+ [九、用于 TensorFlow Eager 时间序列回归的递归神经网络](docs/tf-eager-tut/9.md)
|
||||
+ [TensorFlow 高效编程](docs/effective-tf.md)
|
||||
+ [图嵌入综述:问题,技术与应用](docs/ge-survey-arxiv-1709-07604-zh/README.md)
|
||||
+ [一、引言](docs/ge-survey-arxiv-1709-07604-zh/1.md)
|
||||
+ [三、图嵌入的问题设定](docs/ge-survey-arxiv-1709-07604-zh/2.md)
|
||||
+ [四、图嵌入技术](docs/ge-survey-arxiv-1709-07604-zh/3.md)
|
||||
+ [基于边重构的优化问题](docs/ge-survey-arxiv-1709-07604-zh/4.md)
|
||||
+ [应用](docs/ge-survey-arxiv-1709-07604-zh/5.md)
|
||||
+ [基于深度学习的推荐系统:综述和新视角](docs/rs-survey-arxiv-1707-07435-zh/README.md)
|
||||
+ [引言](docs/rs-survey-arxiv-1707-07435-zh/1.md)
|
||||
+ [基于深度学习的推荐:最先进的技术](docs/rs-survey-arxiv-1707-07435-zh/2.md)
|
||||
+ [基于卷积神经网络的推荐](docs/rs-survey-arxiv-1707-07435-zh/3.md)
|
||||
+ [关于卷积神经网络我们理解了什么](docs/what-do-we-understand-about-convnet/README.md)
|
||||
+ [第1章概论](docs/what-do-we-understand-about-convnet/1.md)
|
||||
+ [第2章多层网络](docs/what-do-we-understand-about-convnet/2.1.1-2.1.3.md)
|
||||
+ [2.1.4生成对抗网络](docs/what-do-we-understand-about-convnet/2.1.4-2.1.6.md)
|
||||
+ [2.2.1最近ConvNets演变中的关键架构](docs/what-do-we-understand-about-convnet/2.2.1.md)
|
||||
+ [2.2.2走向ConvNet不变性](docs/what-do-we-understand-about-convnet/2.2.2-2.2.3.md)
|
||||
+ [2.3时空卷积网络](docs/what-do-we-understand-about-convnet/2.3-2.4.md)
|
||||
+ [第3章了解ConvNets构建块](docs/what-do-we-understand-about-convnet/3.1.md)
|
||||
+ [3.2整改](docs/what-do-we-understand-about-convnet/3.2.md)
|
||||
+ [3.3规范化](docs/what-do-we-understand-about-convnet/3.3.md)
|
||||
+ [3.4汇集](docs/what-do-we-understand-about-convnet/3.4-3.5.md)
|
||||
+ [第四章现状](docs/what-do-we-understand-about-convnet/4.1.md)
|
||||
+ [4.2打开问题](docs/what-do-we-understand-about-convnet/4.2.md)
|
||||
+ [参考](docs/what-do-we-understand-about-convnet/ref.md)
|
||||
+ [机器学习超级复习笔记](docs/super-machine-learning-revision-notes/README.md)
|
||||
+ [Python 迁移学习实用指南](docs/handson-tl-py/README.md)
|
||||
+ [零、前言](docs/handson-tl-py/0.md)
|
||||
+ [一、机器学习基础](docs/handson-tl-py/1.md)
|
||||
+ [二、深度学习基础](docs/handson-tl-py/2.md)
|
||||
+ [三、了解深度学习架构](docs/handson-tl-py/3.md)
|
||||
+ [四、迁移学习基础](docs/handson-tl-py/4.md)
|
||||
+ [五、释放迁移学习的力量](docs/handson-tl-py/5.md)
|
||||
+ [六、图像识别与分类](docs/handson-tl-py/6.md)
|
||||
+ [七、文本文件分类](docs/handson-tl-py/7.md)
|
||||
+ [八、音频事件识别与分类](docs/handson-tl-py/8.md)
|
||||
+ [九、DeepDream](docs/handson-tl-py/9.md)
|
||||
+ [十、自动图像字幕生成器](docs/handson-tl-py/10.md)
|
||||
+ [十一、图像着色](docs/handson-tl-py/11.md)
|
||||
+ [面向计算机视觉的深度学习](docs/dl-cv/README.md)
|
||||
+ [零、前言](docs/dl-cv/00.md)
|
||||
+ [一、入门](docs/dl-cv/01.md)
|
||||
+ [二、图像分类](docs/dl-cv/02.md)
|
||||
+ [三、图像检索](docs/dl-cv/03.md)
|
||||
+ [四、对象检测](docs/dl-cv/04.md)
|
||||
+ [五、语义分割](docs/dl-cv/05.md)
|
||||
+ [六、相似性学习](docs/dl-cv/06.md)
|
||||
+ [七、图像字幕](docs/dl-cv/07.md)
|
||||
+ [八、生成模型](docs/dl-cv/08.md)
|
||||
+ [九、视频分类](docs/dl-cv/09.md)
|
||||
+ [十、部署](docs/dl-cv/10.md)
|
||||
+ [深度学习快速参考](docs/dl-quick-ref/README.md)
|
||||
+ [零、前言](docs/dl-quick-ref/00.md)
|
||||
+ [一、深度学习的基础](docs/dl-quick-ref/01.md)
|
||||
+ [二、使用深度学习解决回归问题](docs/dl-quick-ref/02.md)
|
||||
+ [三、使用 TensorBoard 监控网络训练](docs/dl-quick-ref/03.md)
|
||||
+ [四、使用深度学习解决二分类问题](docs/dl-quick-ref/04.md)
|
||||
+ [五、使用 Keras 解决多分类问题](docs/dl-quick-ref/05.md)
|
||||
+ [六、超参数优化](docs/dl-quick-ref/06.md)
|
||||
+ [七、从头开始训练 CNN](docs/dl-quick-ref/07.md)
|
||||
+ [八、将预训练的 CNN 用于迁移学习](docs/dl-quick-ref/08.md)
|
||||
+ [九、从头开始训练 RNN](docs/dl-quick-ref/09.md)
|
||||
+ [十、使用词嵌入从头开始训练 LSTM](docs/dl-quick-ref/10.md)
|
||||
+ [十一、训练 Seq2Seq 模型](docs/dl-quick-ref/11.md)
|
||||
+ [十二、深度强化学习](docs/dl-quick-ref/12.md)
|
||||
+ [十三、生成对抗网络](docs/dl-quick-ref/13.md)
|
||||
+ [TensorFlow 2.0 快速入门指南](docs/tf-20-quick-start-guide/README.md)
|
||||
+ [零、前言](docs/tf-20-quick-start-guide/00.md)
|
||||
+ [第 1 部分:TensorFlow 2.00 Alpha 简介](docs/tf-20-quick-start-guide/s1.md)
|
||||
+ [一、TensorFlow 2 简介](docs/tf-20-quick-start-guide/01.md)
|
||||
+ [二、Keras:TensorFlow 2 的高级 API](docs/tf-20-quick-start-guide/02.md)
|
||||
+ [三、TensorFlow 2 和 ANN 技术](docs/tf-20-quick-start-guide/03.md)
|
||||
+ [第 2 部分:TensorFlow 2.00 Alpha 中的监督和无监督学习](docs/tf-20-quick-start-guide/s2.md)
|
||||
+ [四、TensorFlow 2 和监督机器学习](docs/tf-20-quick-start-guide/04.md)
|
||||
+ [五、TensorFlow 2 和无监督学习](docs/tf-20-quick-start-guide/05.md)
|
||||
+ [第 3 部分:TensorFlow 2.00 Alpha 的神经网络应用](docs/tf-20-quick-start-guide/s3.md)
|
||||
+ [六、使用 TensorFlow 2 识别图像](docs/tf-20-quick-start-guide/06.md)
|
||||
+ [七、TensorFlow 2 和神经风格迁移](docs/tf-20-quick-start-guide/07.md)
|
||||
+ [八、TensorFlow 2 和循环神经网络](docs/tf-20-quick-start-guide/08.md)
|
||||
+ [九、TensorFlow 估计器和 TensorFlow HUB](docs/tf-20-quick-start-guide/09.md)
|
||||
+ [十、从 tf1.12 转换为 tf2](docs/tf-20-quick-start-guide/10.md)
|
||||
+ [TensorFlow 入门](docs/get-start-tf/README.md)
|
||||
+ [零、前言](docs/get-start-tf/ch00.md)
|
||||
+ [一、TensorFlow 基本概念](docs/get-start-tf/ch01.md)
|
||||
+ [二、TensorFlow 数学运算](docs/get-start-tf/ch02.md)
|
||||
+ [三、机器学习入门](docs/get-start-tf/ch03.md)
|
||||
+ [四、神经网络简介](docs/get-start-tf/ch04.md)
|
||||
+ [五、深度学习](docs/get-start-tf/ch05.md)
|
||||
+ [六、TensorFlow GPU 编程和服务](docs/get-start-tf/ch06.md)
|
||||
+ [TensorFlow 卷积神经网络实用指南](docs/handson-cnn-tf/README.md)
|
||||
+ [零、前言](docs/handson-cnn-tf/0.md)
|
||||
+ [一、TensorFlow 的设置和介绍](docs/handson-cnn-tf/1.md)
|
||||
+ [二、深度学习和卷积神经网络](docs/handson-cnn-tf/2.md)
|
||||
+ [三、TensorFlow 中的图像分类](docs/handson-cnn-tf/3.md)
|
||||
+ [四、目标检测与分割](docs/handson-cnn-tf/4.md)
|
||||
+ [五、VGG,Inception,ResNet 和 MobileNets](docs/handson-cnn-tf/5.md)
|
||||
+ [六、自编码器,变分自编码器和生成对抗网络](docs/handson-cnn-tf/6.md)
|
||||
+ [七、迁移学习](docs/handson-cnn-tf/7.md)
|
||||
+ [八、机器学习最佳实践和故障排除](docs/handson-cnn-tf/8.md)
|
||||
+ [九、大规模训练](docs/handson-cnn-tf/9.md)
|
||||
+ [十、参考文献](docs/handson-cnn-tf/10.md)
|
||||
+ [Python 人工智能中文版](docs/ai-py/README.md)
|
||||
+ [0 前言](docs/ai-py/00.md)
|
||||
+ [1 人工智能简介](docs/ai-py/01.md)
|
||||
+ [2 人工智能的基本用例](docs/ai-py/02.md)
|
||||
+ [3 机器学习管道](docs/ai-py/03.md)
|
||||
+ [4 特征选择和特征工程](docs/ai-py/04.md)
|
||||
+ [5 使用监督学习的分类和回归](docs/ai-py/05.md)
|
||||
+ [6 集成学习的预测分析](docs/ai-py/06.md)
|
||||
+ [7 通过无监督学习检测模式](docs/ai-py/07.md)
|
||||
+ [8 构建推荐系统](docs/ai-py/08.md)
|
||||
+ [9 逻辑编程](docs/ai-py/09.md)
|
||||
+ [10 启发式搜索技术](docs/ai-py/10.md)
|
||||
+ [11 遗传算法和遗传编程](docs/ai-py/11.md)
|
||||
+ [12 云上的人工智能](docs/ai-py/12.md)
|
||||
+ [13 使用人工智能构建游戏](docs/ai-py/13.md)
|
||||
+ [14 构建语音识别器](docs/ai-py/14.md)
|
||||
+ [15 自然语言处理](docs/ai-py/15.md)
|
||||
+ [16 聊天机器人](docs/ai-py/16.md)
|
||||
+ [17 序列数据和时间序列分析](docs/ai-py/17.md)
|
||||
+ [18 图像识别](docs/ai-py/18.md)
|
||||
+ [19 神经网络](docs/ai-py/19.md)
|
||||
+ [20 将卷积神经网络用于深度学习](docs/ai-py/20.md)
|
||||
+ [21 循环神经网络和其他深度学习模型](docs/ai-py/21.md)
|
||||
+ [22 通过强化学习创建智能体](docs/ai-py/22.md)
|
||||
+ [23 人工智能和大数据](docs/ai-py/23.md)
|
||||
+ [Python 无监督学习实用指南](docs/handson-unsup-learn-py/README.md)
|
||||
+ [零、前言](docs/handson-unsup-learn-py/00.md)
|
||||
+ [一、无监督学习入门](docs/handson-unsup-learn-py/01.md)
|
||||
+ [二、聚类基础](docs/handson-unsup-learn-py/02.md)
|
||||
+ [三、高级聚类](docs/handson-unsup-learn-py/03.md)
|
||||
+ [四、实用的层次聚类](docs/handson-unsup-learn-py/04.md)
|
||||
+ [五、软聚类和高斯混合模型](docs/handson-unsup-learn-py/05.md)
|
||||
+ [六、异常检测](docs/handson-unsup-learn-py/06.md)
|
||||
+ [七、降维和成分分析](docs/handson-unsup-learn-py/07.md)
|
||||
+ [八、无监督神经网络模型](docs/handson-unsup-learn-py/08.md)
|
||||
+ [九、生成对抗网络和 SOM](docs/handson-unsup-learn-py/09.md)
|
||||
+ [十、习题](docs/handson-unsup-learn-py/10.md)
|
||||
+ [生成对抗网络项目](docs/gan-proj/README.md)
|
||||
+ [零、前言](docs/gan-proj/0.md)
|
||||
+ [一、生成对抗网络简介](docs/gan-proj/1.md)
|
||||
+ [二、3D-GAN -- 使用 GAN 生成形状](docs/gan-proj/2.md)
|
||||
+ [三、使用条件 GAN 进行人脸老化](docs/gan-proj/3.md)
|
||||
+ [四、使用 DCGAN 生成动漫角色](docs/gan-proj/4.md)
|
||||
+ [五、使用 SRGAN 生成逼真的图像](docs/gan-proj/5.md)
|
||||
+ [六、StackGAN - 逼真的文本到图像合成](docs/gan-proj/6.md)
|
||||
+ [七、CycleGAN - 将绘画变成照片](docs/gan-proj/7.md)
|
||||
+ [八、条件 GAN - 使用条件对抗网络的图像到图像翻译](docs/gan-proj/8.md)
|
||||
+ [九、预测 GAN 的未来](docs/gan-proj/9.md)
|
||||
+ [TensorFlow 智能移动项目](docs/intel-mobi-proj-tf/README.md)
|
||||
+ [零、前言](docs/intel-mobi-proj-tf/00.md)
|
||||
+ [一、移动 TensorFlow 入门](docs/intel-mobi-proj-tf/01.md)
|
||||
+ [二、通过迁移学习对图像进行分类](docs/intel-mobi-proj-tf/02.md)
|
||||
+ [三、检测物体及其位置](docs/intel-mobi-proj-tf/03.md)
|
||||
+ [四、以惊人的艺术风格变换图片](docs/intel-mobi-proj-tf/04.md)
|
||||
+ [五、了解简单的语音命令](docs/intel-mobi-proj-tf/05.md)
|
||||
+ [六、用自然语言描述图像](docs/intel-mobi-proj-tf/06.md)
|
||||
+ [七、使用 CNN 和 LSTM 识别绘画](docs/intel-mobi-proj-tf/07.md)
|
||||
+ [八、用 RNN 预测股价](docs/intel-mobi-proj-tf/08.md)
|
||||
+ [九、使用 GAN 生成和增强图像](docs/intel-mobi-proj-tf/09.md)
|
||||
+ [十、构建类似 AlphaZero 的手机游戏应用](docs/intel-mobi-proj-tf/10.md)
|
||||
+ [十一、在移动设备上使用 TensorFlow Lite 和 Core ML](docs/intel-mobi-proj-tf/11.md)
|
||||
+ [十二、在 Raspberry Pi 上开发 TensorFlow 应用](docs/intel-mobi-proj-tf/12.md)
|
||||
+ [TensorFlow 和 Keras 应用开发入门](docs/begin-app-dev-tf-keras/README.md)
|
||||
+ [零、前言](docs/begin-app-dev-tf-keras/0.md)
|
||||
+ [一、神经网络和深度学习简介](docs/begin-app-dev-tf-keras/1.md)
|
||||
+ [二、模型架构](docs/begin-app-dev-tf-keras/2.md)
|
||||
+ [三、模型评估和优化](docs/begin-app-dev-tf-keras/3.md)
|
||||
+ [四、产品化](docs/begin-app-dev-tf-keras/4.md)
|
||||
+ [TensorFlow 图像深度学习实用指南](docs/handson-dl-img-tf/README.md)
|
||||
+ [零、前言](docs/handson-dl-img-tf/0.md)
|
||||
+ [一、机器学习工具包](docs/handson-dl-img-tf/1.md)
|
||||
+ [二、图片数据](docs/handson-dl-img-tf/2.md)
|
||||
+ [三、经典神经网络](docs/handson-dl-img-tf/3.md)
|
||||
+ [Python 元学习实用指南](docs/handson-meta-learn-py/README.md)
|
||||
+ [零、前言](docs/handson-meta-learn-py/00.md)
|
||||
+ [一、元学习导论](docs/handson-meta-learn-py/01.md)
|
||||
+ [二、使用连体网络的人脸和音频识别](docs/handson-meta-learn-py/02.md)
|
||||
+ [三、原型网络及其变体](docs/handson-meta-learn-py/03.md)
|
||||
+ [四、使用 TensorFlow 的关系和匹配网络](docs/handson-meta-learn-py/04.md)
|
||||
+ [五、记忆增强神经网络](docs/handson-meta-learn-py/05.md)
|
||||
+ [六、MAML 及其变体](docs/handson-meta-learn-py/06.md)
|
||||
+ [七、元 SGD 和 Reptile](docs/handson-meta-learn-py/07.md)
|
||||
+ [八、作为优化目标的梯度一致性](docs/handson-meta-learn-py/08.md)
|
||||
+ [九、最新进展和后续步骤](docs/handson-meta-learn-py/09.md)
|
||||
+ [十、答案](docs/handson-meta-learn-py/10.md)
|
||||
+ [Python 强化学习实用指南](docs/handson-rl-py/README.md)
|
||||
+ [零、前言](docs/handson-rl-py/00.md)
|
||||
+ [一、强化学习导论](docs/handson-rl-py/01.md)
|
||||
+ [二、OpenAI 和 TensorFlow 入门](docs/handson-rl-py/02.md)
|
||||
+ [三、马尔可夫决策过程与动态规划](docs/handson-rl-py/03.md)
|
||||
+ [四、用于游戏的蒙特卡洛方法](docs/handson-rl-py/04.md)
|
||||
+ [五、时间差异学习](docs/handson-rl-py/05.md)
|
||||
+ [六、多臂老虎机问题](docs/handson-rl-py/06.md)
|
||||
+ [七、深度学习基础](docs/handson-rl-py/07.md)
|
||||
+ [八、深度 Q 网络和 Atari 游戏](docs/handson-rl-py/08.md)
|
||||
+ [九、用深度循环 Q 网络玩《毁灭战士》](docs/handson-rl-py/09.md)
|
||||
+ [十、异步优势演员评论家网络](docs/handson-rl-py/10.md)
|
||||
+ [十一、策略梯度和优化](docs/handson-rl-py/11.md)
|
||||
+ [十二、Capstone 项目 – 将 DQN 用于赛车](docs/handson-rl-py/12.md)
|
||||
+ [十三、最新进展和后续步骤](docs/handson-rl-py/13.md)
|
||||
+ [十四、答案](docs/handson-rl-py/14.md)
|
||||
+ [Python 智能项目](docs/intel-proj-py/README.md)
|
||||
+ [零、前言](docs/intel-proj-py/00.md)
|
||||
+ [一、人工智能系统的基础](docs/intel-proj-py/01.md)
|
||||
+ [二、迁移学习](docs/intel-proj-py/02.md)
|
||||
+ [三、神经机器翻译](docs/intel-proj-py/03.md)
|
||||
+ [四、使用 GAN 的时尚行业样式迁移](docs/intel-proj-py/04.md)
|
||||
+ [五、视频字幕应用](docs/intel-proj-py/05.md)
|
||||
+ [六、智能推荐系统](docs/intel-proj-py/06.md)
|
||||
+ [七、电影评论情感分析移动应用](docs/intel-proj-py/07.md)
|
||||
+ [八、用于客户服务的会话式 AI 聊天机器人](docs/intel-proj-py/08.md)
|
||||
+ [九、使用强化学习的自主无人驾驶汽车](docs/intel-proj-py/09.md)
|
||||
+ [十、深度学习视角的验证码](docs/intel-proj-py/10.md)
|
||||
+ [精通 Sklearn 和 TensorFlow 预测性分析](docs/master-pred-anal-sklearn-tf/README.md)
|
||||
+ [零、前言](docs/master-pred-anal-sklearn-tf/0.md)
|
||||
+ [一、回归和分类的集成方法](docs/master-pred-anal-sklearn-tf/1.md)
|
||||
+ [二、交叉验证和参数调整](docs/master-pred-anal-sklearn-tf/2.md)
|
||||
+ [三、使用特征](docs/master-pred-anal-sklearn-tf/3.md)
|
||||
+ [四、人工神经网络和 TensorFlow 简介](docs/master-pred-anal-sklearn-tf/4.md)
|
||||
+ [五、将 TensorFlow 和深度神经网络用于预测分析](docs/master-pred-anal-sklearn-tf/5.md)
|
||||
+ [TensorFlow 2.0 的新增功能](docs/whats-new-tf2/README.md)
|
||||
+ [零、前言](docs/whats-new-tf2/0.md)
|
||||
+ [第 1 部分:TensorFlow 2.0 - 架构和 API 更改](docs/whats-new-tf2/pt1.md)
|
||||
+ [一、TensorFlow 2.0 入门](docs/whats-new-tf2/1.md)
|
||||
+ [二、Keras 默认集成和急切执行](docs/whats-new-tf2/2.md)
|
||||
+ [第 2 部分:TensorFlow 2.0 - 数据和模型训练管道](docs/whats-new-tf2/pt2.md)
|
||||
+ [三、设计和构建输入数据管道](docs/whats-new-tf2/3.md)
|
||||
+ [四、TensorBoard 的模型训练和使用](docs/whats-new-tf2/4.md)
|
||||
+ [第 3 部分:TensorFlow 2.0 - 模型推断和部署以及 AIY](docs/whats-new-tf2/pt3.md)
|
||||
+ [五、模型推理管道 - 多平台部署](docs/whats-new-tf2/5.md)
|
||||
+ [六、AIY 项目和 TensorFlow Lite](docs/whats-new-tf2/6.md)
|
||||
+ [第 4 部分:TensorFlow 2.0 - 迁移,总结](docs/whats-new-tf2/pt4.md)
|
||||
+ [七、从 TensorFlow 1.x 迁移到 2.0](docs/whats-new-tf2/7.md)
|
||||
+ [UCB CS294-112 深度强化学习中文笔记](docs/ucb-cs294-112-notes-zh/README.md)
|
||||
+ [(1) 简介](docs/ucb-cs294-112-notes-zh/1.md)
|
||||
+ [(2) 模仿学习](docs/ucb-cs294-112-notes-zh/2.md)
|
||||
+ [(3) 增强学习简介](docs/ucb-cs294-112-notes-zh/3.md)
|
||||
+ [(4) 策略梯度法](docs/ucb-cs294-112-notes-zh/4.md)
|
||||
+ [(5) 演员-评论家算法](docs/ucb-cs294-112-notes-zh/5.md)
|
||||
+ [(6) 基于值函数的方法](docs/ucb-cs294-112-notes-zh/6.md)
|
||||
+ [(7) 深度增强学习中的 Q 学习方法](docs/ucb-cs294-112-notes-zh/7.md)
|
||||
+ [(8) 最优控制与规划](docs/ucb-cs294-112-notes-zh/8.md)
|
||||
+ [(9) 用数据拟合模型](docs/ucb-cs294-112-notes-zh/9.md)
|
||||
+ [(10) 基于模型的增强学习的策略训练](docs/ucb-cs294-112-notes-zh/10.md)
|
||||
+ [(11) 概率图模型与软化增强学习](docs/ucb-cs294-112-notes-zh/11.md)
|
||||
+ [(12) 逆增强学习](docs/ucb-cs294-112-notes-zh/12.md)
|
||||
+ [TensorFlow 2 和 Keras 高级深度学习](docs/adv-dl-tf2-keras/README.md)
|
||||
+ [零、前言](docs/adv-dl-tf2-keras/00.md)
|
||||
+ [一、使用 Keras 入门高级深度学习](docs/adv-dl-tf2-keras/01.md)
|
||||
+ [二、深度神经网络](docs/adv-dl-tf2-keras/02.md)
|
||||
+ [三、自编码器](docs/adv-dl-tf2-keras/03.md)
|
||||
+ [四、生成对抗网络(GAN)](docs/adv-dl-tf2-keras/04.md)
|
||||
+ [五、改进的 GAN](docs/adv-dl-tf2-keras/05.md)
|
||||
+ [六、纠缠表示 GAN](docs/adv-dl-tf2-keras/06.md)
|
||||
+ [七、跨域 GAN](docs/adv-dl-tf2-keras/07.md)
|
||||
+ [八、变分自编码器(VAE)](docs/adv-dl-tf2-keras/08.md)
|
||||
+ [九、深度强化学习](docs/adv-dl-tf2-keras/09.md)
|
||||
+ [十、策略梯度方法](docs/adv-dl-tf2-keras/10.md)
|
||||
+ [十一、对象检测](docs/adv-dl-tf2-keras/11.md)
|
||||
+ [十二、语义分割](docs/adv-dl-tf2-keras/12.md)
|
||||
+ [十三、使用互信息的无监督学习](docs/adv-dl-tf2-keras/13.md)
|
||||
+ [GCP 上的人工智能实用指南](docs/handson-ai-gcp/README.md)
|
||||
+ [零、前言](docs/handson-ai-gcp/00.md)
|
||||
+ [第 1 节:Google Cloud Platform 的基础](docs/handson-ai-gcp/sec1.md)
|
||||
+ [一、AI 和 GCP 概述](docs/handson-ai-gcp/01.md)
|
||||
+ [二、使用 GCP 组件的计算和处理](docs/handson-ai-gcp/02.md)
|
||||
+ [第 2 节:使用 Google Cloud Platform 的人工智能](docs/handson-ai-gcp/sec2.md)
|
||||
+ [三、XGBoost 的机器学习应用](docs/handson-ai-gcp/03.md)
|
||||
+ [四、使用 Cloud AutoML](docs/handson-ai-gcp/04.md)
|
||||
+ [五、构建大数据云机器学习引擎](docs/handson-ai-gcp/05.md)
|
||||
+ [六、使用 DialogFlow 的智能对话应用](docs/handson-ai-gcp/06.md)
|
||||
+ [第 3 节:Google Cloud Platform 上的 TensorFlow](docs/handson-ai-gcp/sec3.md)
|
||||
+ [七、了解云 TPU](docs/handson-ai-gcp/07.md)
|
||||
+ [八、使用 Cloud ML Engine 实现 TensorFlow 模型](docs/handson-ai-gcp/08.md)
|
||||
+ [九、构建预测应用](docs/handson-ai-gcp/09.md)
|
||||
+ [第 4 节:构建应用和即将发布的功能](docs/handson-ai-gcp/sec4.md)
|
||||
+ [十、构建一个 AI 应用](docs/handson-ai-gcp/10.md)
|
||||
+ [Python 深度学习架构实用指南](docs/handson-dl-arch-py/README.md)
|
||||
+ [零、前言](docs/handson-dl-arch-py/0.md)
|
||||
+ [第 1 节:深度学习的元素](docs/handson-dl-arch-py/sec1.md)
|
||||
+ [一、深度学习入门](docs/handson-dl-arch-py/1.md)
|
||||
+ [二、深度前馈网络](docs/handson-dl-arch-py/2.md)
|
||||
+ [三、受限玻尔兹曼机和自编码器](docs/handson-dl-arch-py/3.md)
|
||||
+ [第 2 节:卷积神经网络](docs/handson-dl-arch-py/sec2.md)
|
||||
+ [四、CNN 架构](docs/handson-dl-arch-py/4.md)
|
||||
+ [五、移动神经网络和 CNN](docs/handson-dl-arch-py/5.md)
|
||||
+ [第 3 节:序列建模](docs/handson-dl-arch-py/sec3.md)
|
||||
+ [六、循环神经网络](docs/handson-dl-arch-py/6.md)
|
||||
+ [第 4 节:生成对抗网络(GAN)](docs/handson-dl-arch-py/sec4.md)
|
||||
+ [七、生成对抗网络](docs/handson-dl-arch-py/7.md)
|
||||
+ [第 5 节:深度学习和高级人工智能的未来](docs/handson-dl-arch-py/sec5.md)
|
||||
+ [八、深度学习的新趋势](docs/handson-dl-arch-py/8.md)
|
||||
+ [Python Web 深度学习实用指南](docs/handson-py-dl-web/README.md)
|
||||
+ [零、前言](docs/handson-py-dl-web/00.md)
|
||||
+ [第 1 节:Web 人工智能](docs/handson-py-dl-web/sec1.md)
|
||||
+ [一、揭秘人工智能和机器学习基础](docs/handson-py-dl-web/01.md)
|
||||
+ [第 2 节:使用深度学习的 Web 开发](docs/handson-py-dl-web/sec2.md)
|
||||
+ [二、使用 Python 入门深度学习](docs/handson-py-dl-web/02.md)
|
||||
+ [三、创建您的第一个深度学习 Web 应用](docs/handson-py-dl-web/03.md)
|
||||
+ [四、TensorFlow.js 入门](docs/handson-py-dl-web/04.md)
|
||||
+ [第 3 节:用于 Web 开发的不同深度学习 API 入门](docs/handson-py-dl-web/sec3.md)
|
||||
+ [五、通过 API 进行深度学习](docs/handson-py-dl-web/05.md)
|
||||
+ [六、Google Cloud Platform 上的 Python 深度学习](docs/handson-py-dl-web/06.md)
|
||||
+ [七、AWS 上的 Python DL:对象检测和家庭自动化](docs/handson-py-dl-web/07.md)
|
||||
+ [八、Microsoft Azure 上的 Python 深度学习](docs/handson-py-dl-web/08.md)
|
||||
+ [第 4 节:生产中的深度学习(智能 Web 应用)](docs/handson-py-dl-web/sec4.md)
|
||||
+ [九、启用深度学习的网站的通用生产框架](docs/handson-py-dl-web/09.md)
|
||||
+ [十、通过深度学习保护 Web 应用安全](docs/handson-py-dl-web/10.md)
|
||||
+ [十一、DIY - Web DL 生产环境](docs/handson-py-dl-web/11.md)
|
||||
+ [十二、使用 DL API 和客户支持聊天机器人创建 E2E Web 应用](docs/handson-py-dl-web/12.md)
|
||||
+ [十三、附录:Web 深度学习的成功案例和新兴领域](docs/handson-py-dl-web/13.md)
|
||||
+ [精通 TensorFlow 2.x 计算机视觉](docs/master-cv-tf-2x/README.md)
|
||||
+ [零、前言](docs/master-cv-tf-2x/0.md)
|
||||
+ [第 1 节:计算机视觉和神经网络概论](docs/master-cv-tf-2x/sec1.md)
|
||||
+ [一、计算机视觉和 TensorFlow 基础知识](docs/master-cv-tf-2x/1.md)
|
||||
+ [二、使用局部二进制模式的内容识别](docs/master-cv-tf-2x/2.md)
|
||||
+ [三、使用 OpenCV 和 CNN 的人脸检测](docs/master-cv-tf-2x/3.md)
|
||||
+ [四、用于图像的深度学习](docs/master-cv-tf-2x/4.md)
|
||||
+ [第 2 节:使用 TensorFlow 的计算机视觉高级概念](docs/master-cv-tf-2x/sec2.md)
|
||||
+ [五、神经网络架构和模型](docs/master-cv-tf-2x/5.md)
|
||||
+ [六、使用迁移学习的视觉搜索](docs/master-cv-tf-2x/6.md)
|
||||
+ [七、YOLO 对象检测](docs/master-cv-tf-2x/7.md)
|
||||
+ [八、语义分割与神经样式迁移](docs/master-cv-tf-2x/8.md)
|
||||
+ [第 3 节:使用 TensorFlow 的计算机视觉的高级实现](docs/master-cv-tf-2x/sec3.md)
|
||||
+ [九、使用多任务深度学习的动作识别](docs/master-cv-tf-2x/9.md)
|
||||
+ [十、R-CNN,SSD 和 R-FCN 对象检测](docs/master-cv-tf-2x/10.md)
|
||||
+ [第 4 节:边缘和云端的 TensorFlow 实现](docs/master-cv-tf-2x/sec4.md)
|
||||
+ [十一、带有 CPU/GPU 优化的边缘设备上的深度学习](docs/master-cv-tf-2x/11.md)
|
||||
+ [十二、用于计算机视觉的云计算平台](docs/master-cv-tf-2x/12.md)
|
||||
+ [TensorFlow Lite,ML Kit 和 Flutter 移动深度学习](docs/mobi-dl-tflite/README.md)
|
||||
+ [零、前言](docs/mobi-dl-tflite/00.md)
|
||||
+ [一、移动深度学习简介](docs/mobi-dl-tflite/01.md)
|
||||
+ [二、移动视觉 - 使用设备上的模型的人脸检测](docs/mobi-dl-tflite/02.md)
|
||||
+ [三、使用 Google Action 的聊天机器人](docs/mobi-dl-tflite/03.md)
|
||||
+ [四、认识植物种类](docs/mobi-dl-tflite/04.md)
|
||||
+ [五、从摄像机源生成实时字幕](docs/mobi-dl-tflite/05.md)
|
||||
+ [六、构建人工智能认证系统](docs/mobi-dl-tflite/06.md)
|
||||
+ [七、语音/多媒体处理 - 使用 AI 生成音乐](docs/mobi-dl-tflite/07.md)
|
||||
+ [八、基于强化神经网络的国际象棋引擎](docs/mobi-dl-tflite/08.md)
|
||||
+ [九、构建图像超分辨率应用](docs/mobi-dl-tflite/09.md)
|
||||
+ [十、前方的路](docs/mobi-dl-tflite/10.md)
|
||||
+ [十一、附录](docs/mobi-dl-tflite/11.md)
|
||||
+ [PyTorch 人工智能研讨会](docs/dl-pt-workshop/README.md)
|
||||
+ [零、前言](docs/dl-pt-workshop/0.md)
|
||||
+ [一、深度学习和 PyTorch 简介](docs/dl-pt-workshop/1.md)
|
||||
+ [二、神经网络的构建块](docs/dl-pt-workshop/2.md)
|
||||
+ [三、使用 DNN 的分类问题](docs/dl-pt-workshop/3.md)
|
||||
+ [四、卷积神经网络](docs/dl-pt-workshop/4.md)
|
||||
+ [五、样式迁移](docs/dl-pt-workshop/5.md)
|
||||
+ [六、使用 RNN 分析数据序列](docs/dl-pt-workshop/6.md)
|
||||
+ [七、附录](docs/dl-pt-workshop/7.md)
|
||||
+ [Python 一次学习实用指南](docs/handson-1shot-learn-py/README.md)
|
||||
+ [零、前言](docs/handson-1shot-learn-py/0.md)
|
||||
+ [第一部分:一次学习简介](docs/handson-1shot-learn-py/sec1.md)
|
||||
+ [一、一次学习简介](docs/handson-1shot-learn-py/1.md)
|
||||
+ [第二部分:深度学习架构](docs/handson-1shot-learn-py/sec2.md)
|
||||
+ [二、基于指标的方法](docs/handson-1shot-learn-py/2.md)
|
||||
+ [三、基于模型的方法](docs/handson-1shot-learn-py/3.md)
|
||||
+ [四、基于优化的方法](docs/handson-1shot-learn-py/4.md)
|
||||
+ [第三部分:其他方法和结论](docs/handson-1shot-learn-py/sec3.md)
|
||||
+ [五、基于生成建模的方法](docs/handson-1shot-learn-py/5.md)
|
||||
+ [六、总结和其他方法](docs/handson-1shot-learn-py/6.md)
|
||||
+ [Python 自然语言处理实用指南](docs/handson-nlp-pt-1x/README.md)
|
||||
+ [零、前言](docs/handson-nlp-pt-1x/0.md)
|
||||
+ [第一部分:用于 NLP 的 PyTorch 1.x 的要点](docs/handson-nlp-pt-1x/sec1.md)
|
||||
+ [一、机器学习和深度学习的基础](docs/handson-nlp-pt-1x/1.md)
|
||||
+ [二、用于 NLP 的 PyTorch 1.x 入门](docs/handson-nlp-pt-1x/2.md)
|
||||
+ [第二部分:自然语言处理基础](docs/handson-nlp-pt-1x/sec2.md)
|
||||
+ [三、NLP 和文本嵌入](docs/handson-nlp-pt-1x/3.md)
|
||||
+ [四、文本预处理,词干提取和词形还原](docs/handson-nlp-pt-1x/4.md)
|
||||
+ [第三部分:使用 PyTorch 1.x 的实际 NLP 应用](docs/handson-nlp-pt-1x/sec3.md)
|
||||
+ [五、循环神经网络和情感分析](docs/handson-nlp-pt-1x/5.md)
|
||||
+ [六、用于文本分类的卷积神经网络](docs/handson-nlp-pt-1x/6.md)
|
||||
+ [七、使用序列到序列神经网络的文本翻译](docs/handson-nlp-pt-1x/7.md)
|
||||
+ [八、使用基于注意力的神经网络构建聊天机器人](docs/handson-nlp-pt-1x/8.md)
|
||||
+ [九、前方的路](docs/handson-nlp-pt-1x/9.md)
|
||||
+ [PyTorch 人工智能基础知识](docs/pt-ai-fund/README.md)
|
||||
+ [零、前言](docs/pt-ai-fund/0.md)
|
||||
+ [一、使用 PyTorch 使用张量](docs/pt-ai-fund/1.md)
|
||||
+ [二、与神经网络协作](docs/pt-ai-fund/2.md)
|
||||
+ [三、用于计算机视觉的卷积神经网络](docs/pt-ai-fund/3.md)
|
||||
+ [四、用于 NLP 的循环神经网络](docs/pt-ai-fund/4.md)
|
||||
+ [五、迁移学习和 TensorBoard](docs/pt-ai-fund/5.md)
|
||||
+ [六、探索生成对抗网络](docs/pt-ai-fund/6.md)
|
||||
+ [七、深度强化学习](docs/pt-ai-fund/7.md)
|
||||
+ [八、在 PyTorch 中生产 AI 模型](docs/pt-ai-fund/8.md)
|
||||
+ [PyTorch 深度学习实用指南](docs/pt-dl-handson/README.md)
|
||||
+ [零、前言](docs/pt-dl-handson/0.md)
|
||||
+ [一、深度学习演练和 PyTorch 简介](docs/pt-dl-handson/1.md)
|
||||
+ [二、简单的神经网络](docs/pt-dl-handson/2.md)
|
||||
+ [三、深度学习工作流程](docs/pt-dl-handson/3.md)
|
||||
+ [四、计算机视觉](docs/pt-dl-handson/4.md)
|
||||
+ [五、序列数据处理](docs/pt-dl-handson/5.md)
|
||||
+ [六、生成网络](docs/pt-dl-handson/6.md)
|
||||
+ [七、强化学习](docs/pt-dl-handson/7.md)
|
||||
+ [八、生产中的 PyTorch ](docs/pt-dl-handson/8.md)
|
||||
+ [TensorFlow 强化学习](docs/rl-tf/README.md)
|
||||
+ [零、前言](docs/rl-tf/00.md)
|
||||
+ [一、深度学习–架构和框架](docs/rl-tf/01.md)
|
||||
+ [二、使用 OpenAI Gym 训练强化学习智能体](docs/rl-tf/02.md)
|
||||
+ [三、马尔可夫决策过程](docs/rl-tf/03.md)
|
||||
+ [四、策略梯度](docs/rl-tf/04.md)
|
||||
+ [五、Q 学习和深度 Q 网络](docs/rl-tf/05.md)
|
||||
+ [六、异步方法](docs/rl-tf/06.md)
|
||||
+ [七、一切都是机器人-真正的战略游戏](docs/rl-tf/07.md)
|
||||
+ [八、AlphaGo –最好的强化学习](docs/rl-tf/08.md)
|
||||
+ [九、自动驾驶中的强化学习](docs/rl-tf/09.md)
|
||||
+ [十、金融投资组合管理](docs/rl-tf/10.md)
|
||||
+ [十一、机器人技术中的强化学习](docs/rl-tf/11.md)
|
||||
+ [十二、广告技术中的深度强化学习](docs/rl-tf/12.md)
|
||||
+ [十三、图像处理中的强化学习](docs/rl-tf/13.md)
|
||||
+ [十四、NLP 中的深度强化学习](docs/rl-tf/14.md)
|
||||
+ [十五、强化学习的其他主题](docs/rl-tf/15.md)
|
||||
@@ -1,137 +0,0 @@
|
||||
# 零、前言
|
||||
|
||||
近年来,深度学习在视觉,语音,自然语言处理和理解以及所有其他领域的大量数据难题中取得了前所未有的成功案例。 公司,大学,政府和研究组织对该领域的兴趣加速了该领域的发展。 本书通过三个新的章节介绍了深度学习中的重要主题:“对象检测”,“语义分割”和“使用互信息的无监督学习”。 通过提供原理的背景知识,挖掘概念背后的直觉,使用 Keras 实现方程式和算法以及检查结果来解释高级理论。
|
||||
|
||||
**人工智能**(**AI**)到今天为止还远远不是一个易于理解的领域。 **深度学习**(**DL**)作为 AI 的子字段,处于相同位置。 尽管它还不是一个成熟的领域,但许多现实世界的应用,例如基于视觉的检测和识别,自主导航,产品推荐,语音识别和合成,节能,药物发现,财务和营销,已经在使用 DL 算法。 。 将发现并构建更多应用。 本书的目的是解释高级概念,提供示例实现,并让作为其领域专家的读者识别目标应用。
|
||||
|
||||
尚未完全成熟的领域是一把双刃剑。 一方面,它为发现和利用提供了很多机会。 深度学习中有许多未解决的问题。 这就意味着可以抢先进入市场的机会–无论是在产品开发,发布还是认可方面。 另一个优势是,在关键任务环境中很难信任一个尚未被完全理解的领域。 我们可以肯定地说,如果被问到,很少有机器学习工程师会乘坐由深度学习系统控制的自动驾驶飞机。 要获得这种信任级别,需要做很多工作。 本书中讨论的高级概念很有可能在获得这种信任级别中扮演重要角色。
|
||||
|
||||
没有 DL 书能够完全涵盖整个领域。 这本书也不例外。 给定时间和空间,我们可能会涉及到有趣的领域,例如自然语言处理和理解,语音合成,自动机器学习(AutoML),图神经网络(GNN),贝叶斯深度学习等等。 但是,本书相信选择和解释选定的区域,以便读者可以从事其他未涵盖的领域。
|
||||
|
||||
作为即将着手阅读本书的读者,请记住,您选择的是一个令人兴奋的领域,会对社会产生巨大影响。 我们很幸运能有一份工作,希望我们在早晨醒来时继续努力。
|
||||
|
||||
# 这本书是给谁的
|
||||
|
||||
本书面向希望更好地了解深度学习高级主题的机器学习工程师和学生。 每个讨论都通过 Keras 中的代码实现进行了补充。 特别是,使用的是 TensorFlow 2 的 Keras API 或简称为`tf.keras`。这本书适合希望了解如何将理论转化为 Keras 中的工作代码实现的读者。 除了理解理论外,代码实现通常是将机器学习应用于实际问题的艰巨任务之一。
|
||||
|
||||
# 本书涵盖的内容
|
||||
|
||||
“第 1 章”,“Keras 高级深度学习入门”涵盖了深度学习的关键概念,例如优化,正则化,损失函数,基本层和网络及其在`tf.keras`中的实现 。 本章回顾了使用顺序 API 的深度学习和`tf.keras`。
|
||||
|
||||
“第 2 章”,“深度神经网络”讨论了`tf.keras`的函数式 API。 使用函数式 API 在`tf.keras`中检查并实现了两种广泛使用的深度网络架构 ResNet 和 DenseNet。
|
||||
|
||||
“第 3 章”,“自编码器”涵盖了一种称为自编码器的通用网络结构,该结构用于发现输入数据的潜在表示形式。 `tf.keras`中讨论并实现了自编码器的两个示例应用,即降噪和着色。
|
||||
|
||||
“第 4 章”,“生成对抗网络(GANs)”讨论了深度学习的最新重大进展之一。 GAN 用于生成看起来真实的新综合数据。 本章介绍 GAN 的原理。 在`tf.keras`中检查并实现了 GAN 的两个示例 DCGAN 和 CGAN。
|
||||
|
||||
“第 5 章”,“改进的 GAN” 涵盖了改进基本 GAN 的算法。 该算法解决了训练 GAN 的困难,并提高了合成数据的感知质量。 在`tf.keras`中讨论并实现了 WGAN,LSGAN 和 ACGAN。
|
||||
|
||||
“第 6 章”,“纠缠表示 GAN” 讨论了如何控制 GAN 生成的合成数据的属性。 如果潜在表示被解开,则可以控制属性。 `tf.keras`中介绍了并实现了两种解开表示的技术,即 InfoGAN 和 StackedGAN。
|
||||
|
||||
“第 7 章”,“跨域 GAN” 涵盖了 GAN 的实际应用,将图像从一个域转换为另一个域,通常称为跨域迁移。 CycleGAN 是一种广泛使用的跨域 GAN,在`tf.keras`中进行了讨论和实现。 本章演示 CycleGAN 执行着色和样式迁移。
|
||||
|
||||
“第 8 章”,“变分自编码器(VAE)”讨论了 DL 中的另一个重要主题。 与 GAN 类似,VAE 是用于生成综合数据的生成模型。 与 GAN 不同,VAE 专注于可解码的连续潜空间,该空间适合于变化推理。 `tf.keras`涵盖并实现了 VAE 及其变体 CVAE 和 β-VAE。
|
||||
|
||||
“第 9 章”,“深度强化学习”解释了强化学习和 Q 学习的原理。 提出了两种实现离散动作空间 Q 学习的技术,即 Q 表更新和**深度 Q 网络**(**DQN**)。 在 OpenAI Gym 环境中演示了在`tf.keras`中使用 Python 和 DQN 进行 Q 学习的实现。
|
||||
|
||||
“第 10 章”,“策略梯度方法”解释了如何使用神经网络来学习强化学习中的决策策略。 在`tf.keras`和 OpenAI Gym 环境中涵盖并实现了四种方法,即 REINFORCE,带有基线的 REINFORCE,演员评论家和优势演员评论家。 本章中的示例演示了连续操作空间上的策略梯度方法。
|
||||
|
||||
“第 11 章”,“对象检测”讨论了计算机视觉,对象检测或识别和定位图像中对象的最常见应用之一。 涵盖了称为 SSD 的多尺度目标检测算法的关键概念,并使用`tf.keras`逐步构建了实现。 提出了用于数据集收集和标记的示例技术。 之后,使用数据集对 SSD 的`tf.keras`实现进行训练和评估。
|
||||
|
||||
“第 12 章”,“语义分割”讨论了计算机视觉,语义分割或识别图像中每个像素的对象类别的另一种常见应用。 讨论了分割原理。 然后,将更详细地介绍语义分割。 使用`tf.keras`构建并评估了称为 FCN 的语义分割算法的示例实现。 使用上一章中收集的相同数据集,但重新标记了语义分割。
|
||||
|
||||
“第 13 章”,“使用互信息的无监督学习”研究了如果 DL 严重依赖人类标签,它将不会继续发展。 无监督学习侧重于不需要人工标签的算法。 一种实现无监督学习的有效技术是利用**互信息**(**MI**)的概念。 通过最大化 MI,可以使用`tf.keras`实现和评估无监督的聚类/分类。
|
||||
|
||||
# 充分利用这本书
|
||||
|
||||
* **深度学习和 Python**:读者应该具有深度学习及其在 Python 中的实现的基础知识。 尽管以前使用 Keras 实现深度学习算法的经验很重要,但这不是必需的。“第 1 章”, “Keras 高级深度学习入门”概述了深度学习的概念及其在`tf.keras`中的实现。
|
||||
* **数学**:本书中的讨论假定读者熟悉大学级别的微积分,线性代数,统计和概率。
|
||||
* **GPU**:本书中的大多数`tf.keras`实现都需要 GPU。 如果没有 GPU,则由于涉及的时间(数小时至数天),因此无法执行许多代码示例。 本书中的示例尽可能多地使用合理数量的数据,以最大程度地减少高性能计算机的使用。 读者应该至少可以使用 NVIDIA GTX 1060。
|
||||
* **编辑器**:本书的示例代码是在 Ubuntu Linux 18.04 LTS 和 MacOS Catalina 中使用 vim 编辑的。 任何支持 Python 的文本编辑器都是可以接受的。
|
||||
* **TensorFlow 2**:本书中的代码示例是使用 TensorFlow 2 的 Keras API 或`tf2`编写的。 请确保正确安装了 NVIDIA GPU 驱动和`tf2`。
|
||||
* **GitHub**:我们通过示例和实验学习。 请从其 GitHub 存储库中`git pull`或`fork`这本书的代码包。 获取代码后,对其进行检查。 运行。 更改。 再次运行。 通过调整代码进行创造性的实验。 这是欣赏本章中解释的所有理论的唯一方法。 在[此书的 GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)上点击星星也受到高度赞赏。
|
||||
|
||||
## 下载示例代码文件
|
||||
|
||||
[本书的代码包托管在 GitHub 上](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
我们还从[这里](https://github.com/PacktPublishing/)提供了丰富的书籍和视频目录中的其他代码包。 去看一下!
|
||||
|
||||
## 下载彩色图像
|
||||
|
||||
我们还提供本书中使用的彩色图像图像。 [您可以在此处下载](https://static.packt-cdn.com/downloads/9787838821654_ColorImages.pdf)。
|
||||
|
||||
## 使用约定
|
||||
|
||||
本书中的代码使用 Python。 更具体地说,是 Python3。例如:
|
||||
|
||||
代码块设置如下:
|
||||
|
||||
```py
|
||||
def build_generator(inputs, image_size):
|
||||
"""Build a Generator Model
|
||||
Stack of BN-ReLU-Conv2DTranpose to generate fake images
|
||||
Output activation is sigmoid instead of tanh in [1].
|
||||
Sigmoid converges easily.
|
||||
Arguments:
|
||||
inputs (Layer): Input layer of the generator
|
||||
the z-vector)
|
||||
image_size (tensor): Target size of one side
|
||||
(assuming square image)
|
||||
Returns:
|
||||
generator (Model): Generator Model
|
||||
"""
|
||||
image_resize = image_size // 4
|
||||
# network parameters
|
||||
kernel_size = 5
|
||||
layer_filters = [128, 64, 32, 1]
|
||||
x = Dense(image_resize * image_resize * layer_filters[0])(inputs)
|
||||
x = Reshape((image_resize, image_resize, layer_filters[0]))(x)
|
||||
for filters in layer_filters:
|
||||
# first two convolution layers use strides = 2
|
||||
# the last two use strides = 1
|
||||
if filters > layer_filters[-2]:
|
||||
strides = 2
|
||||
else:
|
||||
strides = 1
|
||||
x = BatchNormalization()(x)
|
||||
x = Activation('relu')(x)
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')(x)
|
||||
x = Activation('sigmoid')(x)
|
||||
generator = Model(inputs, x, name='generator')
|
||||
return generator
|
||||
```
|
||||
|
||||
当我们希望提请您注意代码块的特定部分时,相关的行或项以粗体显示:
|
||||
|
||||
```py
|
||||
# generate fake images
|
||||
fake_images = generator.predict([noise, fake_labels])
|
||||
# real + fake images = 1 batch of train data
|
||||
x = np.concatenate((real_images, fake_images))
|
||||
# real + fake labels = 1 batch of train data labels
|
||||
labels = np.concatenate((real_labels, fake_labels))
|
||||
```
|
||||
|
||||
只要有可能,都包括文档字符串。 至少,文本注释用于最小化空间使用。
|
||||
|
||||
任何命令行代码执行都编写如下:
|
||||
|
||||
```py
|
||||
python3 dcgan-mnist-4.2.1.py
|
||||
```
|
||||
|
||||
上面的示例具有以下布局:`algorithm-dataset-chapter.section.number.py`。 命令行示例是“第 4 章”,“生成对抗网络(GANs)”第二部分和第一列表中 MNIST 数据集上的 DCGAN。 在某些情况下,未编写要执行的显式命令行,但假定是:
|
||||
|
||||
```py
|
||||
python3 name-of-the-file-in-listing
|
||||
```
|
||||
|
||||
该代码示例的文件名包含在“列表”标题中。 本书使用“列表”标识文本中的代码示例。
|
||||
|
||||
**粗体**:表示新的术语,重要的单词或您在屏幕上看到的单词,例如在菜单或对话框中,也显示在这样的文本中。 例如:StackedGAN 具有两个附加损失函数,即**条件**和**熵**。
|
||||
|
||||
警告或重要提示如下所示。
|
||||
@@ -1,934 +0,0 @@
|
||||
# 一、使用 Keras 入门高级深度学习
|
||||
|
||||
在第一章中,我们将介绍在本书中将使用的三个深度学习人工神经网络。 这些网络是 MLP,CNN 和 RNN(在第 2 节中定义和描述),它们是本书涵盖的所选高级深度学习主题的构建块,例如自回归网络(自编码器,GAN 和 VAE),深度强化学习 ,对象检测和分割以及使用互信息的无监督学习。
|
||||
|
||||
在本章中,我们将一起讨论如何使用 Keras 库实现基于 MLP,CNN 和 RNN 的模型。 更具体地说,我们将使用名为`tf.keras`的 TensorFlow Keras 库。 我们将首先探讨为什么`tf.keras`是我们的理想选择。 接下来,我们将深入研究三个深度学习网络中的实现细节。
|
||||
|
||||
本章将:
|
||||
|
||||
* 确定为什么`tf.keras`库是进行高级深度学习的绝佳选择
|
||||
* 介绍 MLP,CNN 和 RNN –高级深度学习模型的核心构建模块,我们将在本书中使用它们
|
||||
* 提供有关如何使用`tf.keras`实现基于 MLP,CNN 和 RNN 的模型的示例
|
||||
* 在此过程中,开始引入重要的深度学习概念,包括优化,正则化和损失函数
|
||||
|
||||
在本章结束时,我们将使用`tf.keras`实现基本的深度学习网络。 在下一章中,我们将介绍基于这些基础的高级深度学习主题。 让我们通过讨论 Keras 及其作为深度学习库的功能来开始本章。
|
||||
|
||||
# 1\. Keras 为什么是完美的深度学习库?
|
||||
|
||||
Keras [1]是一个受欢迎的深度学习库,在撰写本文时有 370,000 个开发人员在使用它-这个数字每年以大约 35% 的速度增长。 超过 800 位贡献者积极维护它。 我们将在本书中使用的一些示例已添加到 Keras GitHub 官方存储库中。
|
||||
|
||||
谷歌的 TensorFlow 是一个流行的开源深度学习库,它使用 Keras 作为其库的高级 API。 通常称为`tf.keras`。 在本书中,我们将交替使用 Keras 和`tf.keras`一词。
|
||||
|
||||
`tf.keras`作为深度学习库是一种流行的选择,因为它已高度集成到 TensorFlow 中,TensorFlow 因其可靠性而在生产部署中广为人知。 TensorFlow 还提供了各种工具,用于生产部署和维护,调试和可视化以及在嵌入式设备和浏览器上运行模型。 在技术行业中,Google,Netflix,Uber 和 NVIDIA 使用 Keras。
|
||||
|
||||
我们选择`tf.keras`作为本书的首选工具,因为它是致力于加速深度学习模型实现的库。 这使得 Keras 非常适合我们想要实用且动手的时候,例如,当我们探索本书中的高级深度学习概念时。 由于 Keras 旨在加速深度学习模型的开发,训练和验证,因此在有人可以最大限度地利用库之前,必须学习该领域的关键概念。
|
||||
|
||||
[本书的所有示例都可以在 GitHub 的以下链接上找到](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
在`tf.keras`库中,各层之间就像乐高积木一样相互连接,从而形成了一个干净且易于理解的模型。 模型训练非常简单,只需要数据,大量训练和监控指标即可。
|
||||
|
||||
最终结果是,与其他深度学习库(例如 PyTorch)相比,大多数深度学习模型可以用更少的代码行来实现。 通过使用 Keras,我们将通过节省代码实现时间来提高生产率,而这些时间可以用于执行更关键的任务,例如制定更好的深度学习算法。
|
||||
|
||||
同样,Keras 是快速实现深度学习模型的理想选择,就像我们将在本书中使用的那样。 使用**顺序模型 API**,只需几行代码即可构建典型模型。 但是,不要被它的简单性所误导。
|
||||
|
||||
Keras 还可以使用其函数式 API 以及用于动态图的`Model`和`Layer`类来构建更高级和复杂的模型,可以对其进行定制以满足独特的需求。 函数式 API 支持构建类似图的模型,层重用以及创建行为类似于 Python 函数的模型。 同时,`Model`和`Layer`类提供了用于实现罕见或实验性深度学习模型和层的框架。
|
||||
|
||||
## 安装 Keras 和 TensorFlow
|
||||
|
||||
Keras 不是独立的深度学习库。 如您在“图 1.1.1”中所看到的,它建立在另一个深度学习库或后端的之上。 这可能是 Google 的 TensorFlow,MILA 的 Theano,微软的 CNTK 或 Apache MXNet。 但是,与本书的上一版不同,我们将使用 TensorFlow 2.0(`tf2`或简称为`tf`)提供的 Keras(更好地称为`tf.keras`),以利用 tf2 所提供的有用工具。 `tf.keras`也被认为是 TensorFlow 的事实上的前端,它在生产环境中表现出了公认的可靠性。 此外,在不久的将来,将不再提供 Keras 对 TensorFlow 以外的后端的支持。
|
||||
|
||||
从 Keras 迁移到`tf.keras`通常就像更改一样简单:
|
||||
|
||||
```py
|
||||
from keras... import ...
|
||||
```
|
||||
|
||||
至
|
||||
|
||||
```py
|
||||
from tensorflow.keras... import ...
|
||||
```
|
||||
|
||||
本书中的代码示例全部以 **Python 3** 编写,以支持 **Python 2** 于 2020 年结束。
|
||||
|
||||
在硬件上,Keras 在 CPU,GPU 和 Google 的 TPU 上运行。 在本书中,我们将在 CPU 和 NVIDIA GPU(特别是 GTX 1060,GTX 1080Ti,RTX 2080Ti,V100 和 Quadro RTX 8000)上进行测试:
|
||||
|
||||

|
||||
|
||||
图 1.1.1:Keras 是位于其他深度学习框架之上的高级库。 CPU,GPU 和 TPU 支持 Keras。
|
||||
|
||||
在继续进行本书的其余部分之前,我们需要确保正确安装了`tf2`。 有多种执行安装的方法。 一个示例是通过使用`pip3`安装`tf2`:
|
||||
|
||||
```py
|
||||
$ sudo pip3 install tensorflow
|
||||
```
|
||||
|
||||
如果我们具有支持已正确安装驱动的 NVIDIA GPU,以及 NVIDIA CUDA 工具包和 cuDNN 深度神经网络库,则强烈建议您安装启用 GPU 的版本,因为它可以加快训练和预测的速度:
|
||||
|
||||
```py
|
||||
$ sudo pip3 install tensorflow-gpu
|
||||
```
|
||||
|
||||
无需安装 Keras,因为它已经是`tf2`中的包。 如果您不愿意在系统范围内安装库,强烈建议使用 [Anaconda](https://www.anaconda.com/distribution/) 之类的环境。 除了具有隔离环境之外,Anaconda 发行版还安装了用于数据科学的常用第三方包,这些包对于深度学习是必不可少的。
|
||||
|
||||
本书中提供的示例将需要其他包,例如`pydot`,`pydot_ng`,`vizgraph`,`python3-tk`和`matplotlib`。 在继续本章之前,我们需要安装这些包。
|
||||
|
||||
如果安装了`tf2`及其依赖项,则以下内容不会产生任何错误:
|
||||
|
||||
```py
|
||||
$ python3
|
||||
>>> import tensorflow as tf
|
||||
>>> print(tf.__version__)
|
||||
2.0.0
|
||||
>>> from tensorflow.keras import backend as K
|
||||
>>> print(K.epsilon())
|
||||
1e-07
|
||||
```
|
||||
|
||||
本书没有涵盖完整的 Keras API。 我们将仅介绍解释本书中选定的高级深度学习主题所需的材料。 有关更多信息,请查阅 Keras 官方文档,该文档在[这里](https://keras.io)或[这里](https://www.tensorflow.org/guide/keras/overview)。
|
||||
|
||||
在随后的部分中,将讨论 MLP,CNN 和 RNN 的详细信息。 这些网络将用于使用`tf.keras`构建简单的分类器。
|
||||
|
||||
# 2\. MLP,CNN 和 RNN
|
||||
|
||||
我们已经提到,我们将使用三个深度学习网络,它们是:
|
||||
|
||||
* **MLP**:多层感知器
|
||||
* **CNN**:卷积神经网络
|
||||
* **RNN**:循环神经网络
|
||||
|
||||
这些是我们将在本书中使用的三个网络。 稍后,您会发现它们经常结合在一起以利用每个网络的优势。
|
||||
|
||||
在本章中,我们将更详细地讨论这些构建块。 在以下各节中,将介绍 MLP 以及其他重要主题,例如损失函数,优化器和正则化器。 接下来,我们将介绍 CNN 和 RNN。
|
||||
|
||||
## MLP,CNN 和 RNN 之间的区别
|
||||
|
||||
MLP 是**全连接**(**FC**)网络。 在某些文献中,您经常会发现将该称为或深度前馈网络或前馈神经网络。 在本书中,我们将使用术语 MLP。 从已知目标应用的角度了解此网络将有助于我们深入了解高级深度学习模型设计的根本原因。
|
||||
|
||||
MLP 在简单的逻辑和线性回归问题中很常见。 但是,MLP 对于处理顺序和多维数据模式不是最佳的。 通过设计,MLP 难以记住顺序数据中的模式,并且需要大量参数来处理多维数据。
|
||||
|
||||
对于顺序数据输入,RNN 很受欢迎,因为内部设计允许网络发现数据历史记录中的依存关系,这对预测很有用。 对于诸如图像和视频之类的多维数据,CNN 擅长提取用于分类,分割,生成和其他下游任务的特征映射。 在某些情况下,一维卷积形式的 CNN 也用于具有顺序输入数据的网络。 但是,在大多数深度学习模型中,将 MLP 和 CNN 或 RNN 结合起来可以充分利用每个网络。
|
||||
|
||||
MLP,CNN 和 RNN 并不完整整个深度网络。 需要识别**目标**或**损失函数**,**优化器**,和**调节器**。 目标是减少训练期间的损失函数值,因为这样的减少是模型正在学习的一个很好的指标。
|
||||
|
||||
为了使值最小化,模型使用了优化器。 这是一种算法,它确定在每个训练步骤中应如何调整权重和偏差。 经过训练的模型不仅必须对训练数据起作用,而且还必须对训练环境之外的数据起作用。 正则化器的作用是确保训练后的模型能够推广到新数据。
|
||||
|
||||
现在,让我们进入这三个网络–我们将从谈论 MLP 网络开始。
|
||||
|
||||
# 3\. 多层感知器(MLP)
|
||||
|
||||
我们将要看的这三个网络中的第一个是 MLP 网络。 让我们假设目标是创建一个神经网络,用于基于手写数字识别数字。 例如,当网络的输入是手写数字 8 的图像时,相应的预测也必须是数字 8。这是分类器网络的经典工作,可以使用逻辑回归进行训练。 为了训练和验证分类器网络,必须有足够大的手写数字数据集。 *国家标准技术混合研究院*数据集,简称 MNIST [2],通常被视为 **Hello World 深度学习数据集**。 它是用于手写数字分类的合适数据集。
|
||||
|
||||
在我们讨论 MLP 分类器模型之前,必须了解 MNIST 数据集。 本书中的大量示例都使用 MNIST 数据集。 MNIST 用于来解释并验证许多深度学习理论,因为它包含的 70,000 个样本很小,但是的信息足够丰富:
|
||||
|
||||

|
||||
|
||||
图 1.3.1:来自 MNIST 数据集的示例图像。 每个灰度图像为`28×28`像素。
|
||||
|
||||
在下面的中,我们将简要介绍 MNIST。
|
||||
|
||||
## MNIST 数据集
|
||||
|
||||
MNIST 是从 0 到 9 的手写数字的集合。它具有 60,000 张图像的训练集和 10,000 张测试图像,这些图像被分为相应的类别或标签。 在某些文献中,术语**目标**或**基本事实**也用于指**标签**。
|
||||
|
||||
在上图中,可以看到 MNIST 数字的样本图像,每个样本的大小为`28 x 28`像素(灰度)。 为了在 Keras 中使用 MNIST 数据集,提供了一个 API,用于下载并自动提取图像和标签。“列表 1.3.1”演示了如何仅在一行中加载 MNIST 数据集,从而使我们既可以计算训练和测试标签,又可以绘制 25 个随机数字图像。
|
||||
|
||||
“列表 1.3.1”:`mnist-sampler-1.3.1.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.datasets import mnist
|
||||
import matplotlib.pyplot as plt
|
||||
```
|
||||
|
||||
```py
|
||||
# load dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# count the number of unique train labels
|
||||
unique, counts = np.unique(y_train, return_counts=True)
|
||||
print("Train labels: ", dict(zip(unique, counts)))
|
||||
```
|
||||
|
||||
```py
|
||||
# count the number of unique test labels
|
||||
unique, counts = np.unique(y_test, return_counts=True)
|
||||
print("Test labels: ", dict(zip(unique, counts)))
|
||||
```
|
||||
|
||||
```py
|
||||
# sample 25 mnist digits from train dataset
|
||||
indexes = np.random.randint(0, x_train.shape[0], size=25)
|
||||
images = x_train[indexes]
|
||||
labels = y_train[indexes]
|
||||
```
|
||||
|
||||
```py
|
||||
# plot the 25 mnist digits
|
||||
plt.figure(figsize=(5,5))
|
||||
for i in range(len(indexes)):
|
||||
plt.subplot(5, 5, i + 1)
|
||||
image = images[i]
|
||||
plt.imshow(image, cmap='gray')
|
||||
plt.axis('off')
|
||||
```
|
||||
|
||||
```py
|
||||
plt.savefig("mnist-samples.png")
|
||||
plt.show()
|
||||
plt.close('all')
|
||||
```
|
||||
|
||||
`mnist.load_data()`方法很方便,因为不需要分别加载所有 70,000 张图像和标签并将它们存储在数组中。 执行以下命令:
|
||||
|
||||
```py
|
||||
python3 mnist-sampler-1.3.1.py
|
||||
```
|
||||
|
||||
在命令行上,该代码示例打印训练和测试数据集中的标签分布:
|
||||
|
||||
```py
|
||||
Train labels:{0: 5923, 1: 6742, 2: 5958, 3: 6131, 4: 5842, 5: 5421, 6: 5918, 7: 6265, 8: 5851, 9: 5949}
|
||||
Test labels:{0: 980, 1: 1135, 2: 1032, 3: 1010, 4: 982, 5: 892, 6: 958, 7: 1028, 8: 974, 9: 1009}
|
||||
```
|
||||
|
||||
之后,代码将绘制 25 个随机数字,如先前在“图 1.3.1”中所示。
|
||||
|
||||
在讨论 MLP 分类器模型之前,必须记住,虽然 MNIST 数据由二维张量组成,但应根据输入层的类型对它进行重塑。 以下“图 1.3.2”显示了如何为 MLP,CNN 和 RNN 输入层重塑`3×3`灰度图像:
|
||||
|
||||

|
||||
|
||||
图 1.3.2:根据输入层的类型,对与 MNIST 数据相似的输入图像进行重塑。 为简单起见,显示了`3×3`灰度图像的重塑。
|
||||
|
||||
在以下各节中,将介绍 MNIST 的 MLP 分类器模型。 我们将演示如何使用`tf.keras`有效地构建,训练和验证模型。
|
||||
|
||||
## MNIST 数字分类器模型
|
||||
|
||||
“图 1.3.3”中显示的建议的 MLP 模型可用于 MNIST 数字分类。 当单元或感知器暴露在外时,MLP 模型是一个全连接网络,如图“图 1.3.4”所示。 我们还将展示如何根据第`n`个单元的权重`w[i]`和偏置`b[n]`的输入来计算感知器的输出。 相应的`tf.keras`实现在“列表 1.3.2”中进行了说明:
|
||||
|
||||

|
||||
|
||||
图 1.3.3:MLP MNIST 数字分类器模型
|
||||
|
||||

|
||||
|
||||
图 1.3.4:图 1.3.3 中的 MLP MNIST 数字分类器由全连接层组成。 为简单起见,未显示激活层和退出层。 还详细显示了一个单元或感知器。
|
||||
|
||||
“列表 1.3.2”:`mlp-mnist-1.3.2.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.models import Sequential
|
||||
from tensorflow.keras.layers import Dense, Activation, Dropout
|
||||
from tensorflow.keras.utils import to_categorical, plot_model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
```
|
||||
|
||||
```py
|
||||
# load mnist dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# compute the number of labels
|
||||
num_labels = len(np.unique(y_train))
|
||||
```
|
||||
|
||||
```py
|
||||
# convert to one-hot vector
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
|
||||
# image dimensions (assumed square)
|
||||
image_size = x_train.shape[1]
|
||||
input_size = image_size * image_size
|
||||
```
|
||||
|
||||
```py
|
||||
# resize and normalize
|
||||
x_train = np.reshape(x_train, [-1, input_size])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = np.reshape(x_test, [-1, input_size])
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
batch_size = 128
|
||||
hidden_units = 256
|
||||
dropout = 0.45
|
||||
```
|
||||
|
||||
```py
|
||||
# model is a 3-layer MLP with ReLU and dropout after each layer
|
||||
model = Sequential()
|
||||
model.add(Dense(hidden_units, input_dim=input_size))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(dropout))
|
||||
model.add(Dense(hidden_units))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(dropout))
|
||||
model.add(Dense(num_labels))
|
||||
# this is the output for one-hot vector
|
||||
model.add(Activation('softmax'))
|
||||
model.summary()
|
||||
plot_model(model, to_file='mlp-mnist.png', show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# loss function for one-hot vector
|
||||
# use of adam optimizer
|
||||
# accuracy is good metric for classification tasks
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
# train the network
|
||||
model.fit(x_train, y_train, epochs=20, batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
# validate the model on test dataset to determine generalization
|
||||
_, acc = model.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=0)
|
||||
print("\nTest accuracy: %.1f%%" % (100.0 * acc))
|
||||
```
|
||||
|
||||
在讨论模型实现之前,数据必须具有正确的形状和格式。 加载 MNIST 数据集后,标签的数量计算为:
|
||||
|
||||
```py
|
||||
# compute the number of labels
|
||||
num_labels = len(np.unique(y_train))
|
||||
```
|
||||
|
||||
硬编码`num_labels = 10`也可以选择。 但是,让计算机完成工作始终是一个好习惯。 该代码假定`y_train`的标签为 0 到 9。
|
||||
|
||||
此时,标签为数字格式,即从 0 到 9。标签的这种稀疏标量表示形式不适用于按类别输出概率的神经网络预测层。 一种更合适的格式称为`one-hot vector`,这是一个十维向量,除数字类的索引外,所有元素均为 0。 例如,如果标签为 2,则等效`one-hot vector`为[0,0,1,0,0,0,0,0,0,0]。 第一个标签的索引为 0。
|
||||
|
||||
以下各行将每个标签转换为`one-hot vector`:
|
||||
|
||||
```py
|
||||
# convert to one-hot vector
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
```
|
||||
|
||||
在深度学习中,数据存储在张量中。 张量一词适用于标量(0D 张量),向量(1D 张量),矩阵(二维张量)和多维张量。
|
||||
|
||||
从这一点出发,除非标量,向量或矩阵使解释更清楚,否则将使用术语张量。
|
||||
|
||||
如下所示的其余代码将计算图像尺寸,第一密集层的`input_size`值,并将每个像素值从 0 缩放到 255,范围从 0.0 缩放到 1.0。 尽管可以直接使用原始像素值,但最好对输入数据进行规范化,以避免产生可能会使训练变得困难的较大梯度值。 网络的输出也被标准化。 训练后,可以通过将输出张量乘以 255 来将所有内容恢复为整数像素值。
|
||||
|
||||
提出的模型基于 MLP 层。 因此,输入应为一维张量。 这样,将`x_train`和`x_test`分别重塑为`[60,000,28 * 28]`和`[10,000,28 * 28]`。 在 NumPy 中,大小为 -1 表示让库计算正确的尺寸。 在`x_train`的情况下为 60,000。
|
||||
|
||||
```py
|
||||
# image dimensions (assumed square) 400
|
||||
image_size = x_train.shape[1]
|
||||
input_size = image_size * image_size
|
||||
```
|
||||
|
||||
```py
|
||||
# resize and normalize
|
||||
x_train = np.reshape(x_train, [-1, input_size])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = np.reshape(x_test, [-1, input_size])
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
在准备好数据集之后,以下内容将重点介绍使用 Keras 的顺序 API 构建 MLP 分类器模型。
|
||||
|
||||
## 使用 MLP 和 Keras 构建模型
|
||||
|
||||
数据准备之后,接下来是构建模型。 所提出的模型由三个 MLP 层组成。 在 Keras 中,将 MLP 层称为**密集**,它表示紧密连接的层。 第一和第二个 MLP 层本质上是相同的,每个都有 256 个单元,然后是**整流线性单元**(**ReLU**)激活和退出。 由于 128、512 和 1,024 个单元的表现指标较低,因此选择 256 个单元。 在 128 个单元的情况下,网络收敛迅速,但测试精度较低。 512 或 1,024 的额外单元数量不会显着提高测试精度。
|
||||
|
||||
单元数是超参数。 它控制网络的**容量**。 容量是网络可以近似的函数复杂性的度量。 例如,对于多项式,度是超参数。 随着程度的增加,函数的能力也随之增加。
|
||||
|
||||
如以下代码行所示,使用 Keras 的顺序 API 实现分类器模型。 如果模型需要一个输入和一个输出(由一系列层处理),这就足够了。 为了简单起见,我们现在将使用它。 但是,在“第 2 章”,“深度神经网络”中,将引入 Keras 的函数式 API 来实现高级深度学习模型,该模型需要更复杂的结构(例如多个输入和输出)。
|
||||
|
||||
```py
|
||||
# model is a 3-layer MLP with ReLU and dropout after each layer model = Sequential()
|
||||
model.add(Dense(hidden_units, input_dim=input_size))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(dropout))
|
||||
model.add(Dense(hidden_units))
|
||||
model.add(Activation('relu'))
|
||||
model.add(Dropout(dropout))
|
||||
model.add(Dense(num_labels))
|
||||
# this is the output for one-hot vector model.add(Activation('softmax'))
|
||||
```
|
||||
|
||||
由于`Dense`层是线性运算,因此`Dense`层的序列只能近似线性函数。 问题是 MNIST 数字分类本质上是非线性过程。 在`Dense`层之间插入`relu`激活将使 MLP 网络能够对非线性映射建模。 `relu`或 ReLU 是一个简单的非线性函数。 这很像一个过滤器,它允许正输入不变地通过,同时将其他所有值都钳位为零。 数学上,`relu`用以下公式表示,见“图 1.3.5”:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
图 1.3.5:ReLU 函数图。 ReLU 函数在神经网络中引入了非线性。
|
||||
|
||||
还可以使用其他非线性函数,例如`elu`,`selu`,`softplus`,`sigmoid`和`tanh`。 但是,`relu`是最常用的函数,由于其简单性,在计算上是有效的。 Sigmoid 和 tanh 函数在输出层中用作激活函数,稍后将描述。“表 1.3.1”显示了每个激活函数的方程式:
|
||||
|
||||
| `relu` | `relu(x) = max(0, x)` | 1.3.1 |
|
||||
| --- | --- | --- |
|
||||
| `softplus` | `softplus(x) = log(1 + exp(x))` | 1.3.2 |
|
||||
| `elu` |  其中`a≥0`并且是可调超参数 | 1.3.3 |
|
||||
| `selu` | `selu(x) = k×elu(x, a)`其中`k = 1.0507009873554804934193193349852946`和`a = 1.6732632423543772848170429916717` | 1.3.4 |
|
||||
| `sigmoid` |  | 1.3.5 |
|
||||
| `tanh` |  | 1.3.6 |
|
||||
|
||||
表 1.3.1:常见非线性激活函数的定义
|
||||
|
||||
尽管我们已完成 MLP 分类器模型的关键层,但我们尚未解决泛化问题或模型超出训练数据集的能力。 为了解决这个问题,我们将在下一节介绍正则化。
|
||||
|
||||
## 正则化
|
||||
|
||||
神经网络倾向于记住其训练数据,特别是如果它包含的容量超过。 在这种情况下,当经受测试数据时,网络将发生灾难性的故障。 这是网络无法推广的经典情况。 为了避免这种趋势,模型使用了正则化层或函数。 常见的正则化层是`Dropout`。
|
||||
|
||||
丢弃的想法很简单。 给定丢弃率(此处将其设置为`dropout = 0.45`),丢弃层会从参与下一层的单元中随机删除这一部分。 例如,如果第一层具有 256 个单元,则在应用`dropout = 0.45`之后,只有`(1-0.45) * 256`个单元,来自第 1 层的 140 个单元参与第 2 层。
|
||||
|
||||
丢弃层使神经网络对于无法预见的输入数据具有鲁棒性,因为即使缺少某些单元,训练后的神经网络也可以正确预测。 值得注意的是,输出层中没有使用丢弃,它仅在训练期间处于活动状态。 此外,在预测期间不存在丢弃现象。
|
||||
|
||||
除了诸如丢弃之类的正则化之外,还可以使用其他正则化器。 在 Keras 中,可以按层对偏置,权重和激活输出进行正则化。 `l1`和`l2`通过添加罚函数来支持较小的参数值。 `l1`和`l2`都使用绝对值(`l1`)或平方(`l2`)之和的分数来执行惩罚。 换句话说,惩罚函数迫使优化器找到较小的参数值。 参数值小的神经网络对来自输入数据的噪声的存在更加不敏感。
|
||||
|
||||
例如,带有`fraction=0.001`的`l2`权重正则器可以实现为:
|
||||
|
||||
```py
|
||||
from tensorflow.keras.regularizers import l2
|
||||
model.add(Dense(hidden_units,
|
||||
kernel_regularizer=l2(0.001),
|
||||
input_dim=input_size))
|
||||
```
|
||||
|
||||
如果使用`l1`或`l2`正则化,则不添加任何附加层。 正则化在内部施加在`Dense`层中。 对于建议的模型,丢弃仍然具有比`l2`更好的表现。
|
||||
|
||||
我们的模型几乎已经完成。 下一节将重点介绍输出层和损失函数。
|
||||
|
||||
## 输出激活和损失函数
|
||||
|
||||
输出的层具有 10 个单元,其后是`softmax`激活层。 这 10 个单元对应于 10 个可能的标签,类或类别。 可以用数学方式表示`softmax`激活,如以下等式所示:
|
||||
|
||||
 (Equation 1.3.7)
|
||||
|
||||
该方程适用于所有`N = 10`输出,`x[i]`对于`i = 0, 1, ..., 9`作最终预测。 `softmax`的概念非常简单。 通过对预测进行归一化,将输出压缩为概率。 在此,每个预测输出都是该索引是给定输入图像的正确标签的概率。 所有输出的所有概率之和为 1.0。 例如,当`softmax`层生成预测时,它将是一个 10 维一维张量,看起来像以下输出:
|
||||
|
||||
```py
|
||||
[3.57351579e-11 7.08998016e-08
|
||||
2.30154569e-07 6.35787558e-07
|
||||
5.57471187e-11 4.15353840e-09
|
||||
3.55973775e-16 9.99995947e-01
|
||||
1.29531730e-09 3.06023480e-06]
|
||||
```
|
||||
|
||||
预测输出张量建议输入图像的索引具有最高概率,因此将为 7。 `numpy.argmax()`方法可用于确定具有最高值的元素的索引。
|
||||
|
||||
输出激活层还有其他选择,例如`linear`,`sigmoid`或`tanh`。 `linear`激活是一种恒等函数。 它将其输入复制到其输出。 `sigmoid`函数更具体地是,称为**逻辑 Sigmoid**。 如果预测张量的元素将独立地映射在 0.0 和 1.0 之间,则将使用此方法。 与`softmax`中不同,预测张量的所有元素的总和不限于 1.0。 例如,`sigmoid`用作情感预测(从 0.0 到 1.0、0.0 不好,1.0 很好)或图像生成(0.0 映射到像素级别 0 和 1.0 映射到像素 255)的最后一层 。
|
||||
|
||||
`tanh`函数将其输入映射在 -1.0 到 1.0 的范围内。 如果输出可以同时以正值和负值摆幅,则这一点很重要。 `tanh`函数在循环神经网络的内部层中更普遍使用,但也已用作输出层激活。 如果在输出激活中使用 tanh 代替`sigmoid`,则必须适当缩放使用的数据。 例如,不是使用`x = x / 255`缩放`[0.0, 1.0]`范围内的每个灰度像素,而是使用`x = (x - 127.5) / 127.5`将其分配在`[-1.0, 1.0]`范围内。
|
||||
|
||||
下图“图 1.3.6”显示了`sigmoid`和`tanh`函数。 数学上,Sigmoid 可以用以下公式表示:
|
||||
|
||||
 (Equation 1.3.5)
|
||||
|
||||

|
||||
|
||||
图 1.3.6:Sigmoid 和正切图
|
||||
|
||||
预测张量距单热地面真值向量有多远称为损失。 损失函数的一种类型是`mean_squared_error`(**MSE**),或者是目标或标签与预测之间差异的平方的平均值。 在当前示例中,我们使用`categorical_crossentropy`。 它是目标或标签乘积与每个类别的预测对数之和的负数。 Keras 中还有其他损失函数,例如`mean_absolute_error`和`binary_crossentropy`。“表 1.3.2”总结了的常见损失函数。
|
||||
|
||||
| **损失函数** | **公式** |
|
||||
| --- | --- |
|
||||
| `mean_squared_error` |  |
|
||||
| `mean_absolute_error` |  |
|
||||
| `categorical_crossentropy` |  |
|
||||
| `binary_crossentropy` |  |
|
||||
|
||||
表 1.3.2:常见损失函数汇总。 类别是指标签和预测中的类别数(例如:MNIST 为 10)。 所示的损失方程式仅适用于一个输出。 平均损失值是整个批量的平均值。
|
||||
|
||||
损失函数的选择不是任意的,而应作为模型正在学习的标准。 对于按类别进行分类,在`softmax`激活层之后,`categorical_crossentropy`或`mean_squared_error`是一个不错的选择。 `binary_crossentropy`损失函数通常在`sigmoid`激活层之后使用,而`mean_squared_error`是`tanh`输出的选项。
|
||||
|
||||
在下一部分中,我们将讨论优化算法以最小化我们在此处讨论的损失函数。
|
||||
|
||||
## 优化
|
||||
|
||||
通过优化,目标是使损失函数最小化。 这个想法是,如果将损失减少到可接受的水平,则该模型将间接学习将输入映射到输出的函数。 表现指标用于确定模型是否了解了基础数据分布。 Keras 中的默认指标是**损失**。 在训练,验证和测试期间,还可以包括其他指标,例如**准确率**。 准确率是基于地面真实性的正确预测的百分比或分数。 在深度学习中,还有许多其他表现指标。 但是,它取决于模型的目标应用。 在文献中,报告了**测试数据集**上训练后的模型的表现指标,用于与其他深度学习模型进行比较。
|
||||
|
||||
在 Keras 中,优化器有个选择。 最常用的优化器是**随机梯度下降**(**SGD**),**自适应矩**(**Adam**)和**均方根传播**(**RMSprop**)。 每个优化器均具有可调参数,例如学习率,动量和衰减。 Adam 和 RMSprop 是具有自适应学习率的 SGD 的变体。 在提出的分类器网络中,使用了 Adam,因为它具有最高的测试精度。
|
||||
|
||||
SGD 被认为是最基本的优化程序。 它是演算中梯度下降的简单版本。 在**梯度下降**(**GD**)中,追踪下坡函数的曲线可找到最小值,就像在山谷中下坡直至到达底部一样。
|
||||
|
||||
GD 算法如图 1.3.7 所示。 假设`x`是被调整以找到`y`的最小值(例如,损失函数)的参数(例如,权重)。 从`x = -0.5`的任意点开始。 梯度`dy/dx = -2.0`。 GD 算法强加`x`然后更新为`x = -0.5 - ε(-2.0)`。 `x`的新值等于旧值,再加上`ε`缩放的梯度的相反值。 小数字`ε`是指学习率。 如果`ε = 0.01`,则`x`的新值为 -0.48。 GD 是迭代执行的。 在每一步,`y`都将接近其最小值。 在`x = 0.5`时,`dy/dx = 0`。 GD 已找到`y = -1.25`的绝对最小值。 梯度建议不要进一步改变`x`。
|
||||
|
||||
学习率的选择至关重要。 大的`ε`值可能找不到最小值,因为搜索只会在最小值附近来回摆动。 一方面,在找到最小值之前,较大的`ε`值可能需要进行大量迭代。 在有多个最小值的情况下,搜索可能会陷入局部最小值。
|
||||
|
||||

|
||||
|
||||
图 1.3.7:GD 类似于在函数曲线上向下走直到到达最低点。 在此图中,全局最小值为`x = 0.5`。
|
||||
|
||||
多个极小值的示例可以在“图 1.3.8”中看到。 如果由于某种原因从图的左侧开始搜索并且学习率很小,则 GD 很可能会发现`x = -1.51`是*最小值* 。 GD 无法在`x = 1.66`时找到全局最小值。 具有足够值的学习率将使 GD 可以克服`x = 0.0`的问题。
|
||||
|
||||
在深度学习实践中,通常建议从更高的学习率开始(例如,从 0.1 到 0.001),并随着损失接近最小值而逐渐降低学习率。
|
||||
|
||||

|
||||
|
||||
图 1.3.8:具有 2 个最小值的函数图,`x = -1.51`和`x = 1.66`。 还显示了该函数的导数。
|
||||
|
||||
GD 通常不用于深度神经网络,因为遇到数百万个要训练的参数很常见。 执行完整的 GD 在计算上效率低下。 而是使用 SGD。 在 SGD 中,选择一小批样本以计算下降的近似值。 参数(例如权重和偏差)可通过以下公式进行调整:
|
||||
|
||||

|
||||
|
||||
在该等式中,`θ`和`g = 1/m ᐁ[θ] ΣL`分别是损失函数的参数和梯度张量。`g`由损失函数的偏导数计算得出。 出于 GPU 优化的目的,建议最小批量大小为 2 的幂。 在建议的网络中,`batch_size = 128`。
|
||||
|
||||
“公式 1.3.8”计算最后一层参数更新。 那么,我们如何调整前几层的参数呢? 在这种情况下,应用微分链规则将导数传播到较低层并相应地计算梯度。 该算法在深度学习中称为**反向传播**。 反向传播的详细信息超出了本书的范围。 但是,可以在[这里](http://neuralnetworksanddeeplearning.com)找到很好的在线参考。
|
||||
|
||||
由于优化是基于微分的,因此得出损失函数的重要标准是它必须平滑或可微。 当引入新的损失函数时,这是要牢记的重要约束。
|
||||
|
||||
给定训练数据集,损失函数的选择,优化器和正则化器,现在可以通过调用`fit()`函数来训练模型:
|
||||
|
||||
```py
|
||||
# loss function for one-hot vector
|
||||
# use of adam optimizer
|
||||
# accuracy is a good metric for classification tasks model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam', metrics=['accuracy'])
|
||||
```
|
||||
|
||||
```py
|
||||
# train the network
|
||||
model.fit(x_train, y_train, epochs=20, batch_size=batch_size)
|
||||
```
|
||||
|
||||
这是 Keras 的另一个有用函数。 通过仅提供`x`和`y`数据,要训练的周期数和批量大小,`fit()`完成了其余工作。 在其他深度学习框架中,这转化为多项任务,例如以适当的格式准备输入和输出数据,加载,监视等等。 尽管所有这些都必须在`for`循环内完成,但在 Keras 中,一切都只需要一行即可完成。
|
||||
|
||||
在`fit()`函数中,一个周期是整个训练数据的完整采样。 `batch_size`参数是每个训练步骤要处理的输入数量的样本大小。 为了完成一个周期,`fit()`将处理等于训练数据集大小的步数除以批量大小再加上 1,以补偿任何小数部分。
|
||||
|
||||
训练模型后,我们现在可以评估其表现。
|
||||
|
||||
## 表现评估
|
||||
|
||||
至此,MNIST 数字分类器的模型现已完成。 表现评估将是的下一个关键步骤,以确定提议的训练模型是否已提出令人满意的解决方案。 将模型训练 20 个时间段就足以获得可比较的表现指标。
|
||||
|
||||
下表“表 1.3.3”列出了不同的网络配置和相应的表现指标。 在“层”下,显示第 1 到第 3 层的单元数。对于每个优化器,将使用`tf.keras`中的默认参数。 可以观察到改变正则化器,优化器和每层单元数的效果。“表 1.3.3”中的另一个重要观察结果是,更大的网络不一定会转化为更好的表现。
|
||||
|
||||
在训练和测试数据集的准确率方面,增加此网络的深度不会显示任何其他好处。 另一方面,较少的单元(例如 128)也可能会降低测试和训练的准确率。 删除正则器后,将在`99.93%`处获得最佳的训练精度,并且每层使用 256 个单元。 但是,由于网络过拟合,测试精度在`98.0%`时要低得多。
|
||||
|
||||
最高的测试精度是使用 Adam 优化器和`98.5%`处的`Dropout(0.45)`。 从技术上讲,鉴于其训练精度为`99.39%`,仍然存在某种程度的过拟合。 对于`256-512-256`,`Dropout(0.45)`和 SGD,在`98.2%`时,训练和测试精度均相同。 同时去除正则化和 ReLU 层会导致其表现最差。 通常,我们会发现`Dropout`层比`l2`具有更好的表现。
|
||||
|
||||
下表演示了调整期间典型的深度神经网络表现:
|
||||
|
||||
| **层** | **正则化函数** | **优化器** | **ReLU** | **训练准确率(%)** | **测试准确率(%)** |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 256-256-256 | 没有 | SGD | 没有 | 93.65 | 92.5 |
|
||||
| 256-256-256 | L2(0.001) | SGD | 是 | 99.35 | 98.0 |
|
||||
| 256-256-256 | L2(0.01) | SGD | 是 | 96.90 | 96.7 |
|
||||
| 256-256-256 | 没有 | SGD | 是 | 99.93 | 98.0 |
|
||||
| 256-256-256 | 丢弃(0.4) | SGD | 是 | 98.23 | 98.1 |
|
||||
| 256-256-256 | 丢弃(0.45) | SGD | 是 | 98.07 | 98.1 |
|
||||
| 256-256-256 | 丢弃(0.5) | SGD | 是 | 97.68 | 98.1 |
|
||||
| 256-256-256 | 丢弃(0.6) | SGD | 是 | 97.11 | 97.9 |
|
||||
| 256-512-256 | 丢弃(0.45) | SGD | 是 | 98.21 | 98.2 |
|
||||
| 512-512-512 | 丢弃(0.2) | SGD | 是 | 99.45 | 98.3 |
|
||||
| 512-512-512 | 丢弃(0.4) | SGD | 是 | 98.95 | 98.3 |
|
||||
| 512-1024-512 | 丢弃(0.45) | SGD | 是 | 98.90 | 98.2 |
|
||||
| 1024-1024-1024 | 丢弃(0.4) | SGD | 是 | 99.37 | 98.3 |
|
||||
| 256-256-256 | 丢弃(0.6) | Adam | 是 | 98.64 | 98.2 |
|
||||
| 256-256-256 | 丢弃(0.55) | Adam | 是 | 99.02 | 98.3 |
|
||||
| 256-256-256 | 丢弃(0.45) | Adam | 是 | 99.39 | 98.5 |
|
||||
| 256-256-256 | 丢弃(0.45) | RMSprop | 是 | 98.75 | 98.1 |
|
||||
| 128-128-128 | 丢弃(0.45) | Adam | 是 | 98.70 | 97.7 |
|
||||
|
||||
表 1.3.3 不同的 MLP 网络配置和表现指标
|
||||
|
||||
示例指示需要改进网络架构。 在下一节讨论了 MLP 分类器模型摘要之后,我们将介绍另一个 MNIST 分类器。 下一个模型基于 CNN,并证明了测试准确率的显着提高。
|
||||
|
||||
## 模型摘要
|
||||
|
||||
使用 Keras 库为我们提供了一种快速的机制,可以通过调用以下方法来仔细检查模型描述:
|
||||
|
||||
```py
|
||||
model.summary()
|
||||
```
|
||||
|
||||
下面的“列表 1.3.3”显示了所建议网络的模型摘要。 它总共需要 269,322 个参数。 考虑到我们具有对 MNIST 数字进行分类的简单任务,这一点非常重要。 MLP 的参数效率不高。 可以通过关注如何计算感知器的输出,从“图 1.3.4”计算参数的数量。 从输入到密集层:`784 × 256 + 256 = 200,960`。 从第一密集层到第二密集层:`256 × 256 + 256 = 65,792`。 从第二个密集层到输出层:`10 × 256 + 10 = 2,570`。 总数是`269,322`。
|
||||
|
||||
“列表 1.3.3”:MLP MNIST 数字分类器模型的摘要:
|
||||
|
||||
```py
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
dense_1 (Dense) (None, 256) 200960
|
||||
activation_1 (Activation) (None, 256) 0
|
||||
dropout_1 (Dropout) (None, 256) 0
|
||||
dense_2 (Dense) (None, 256) 65792
|
||||
activation_2 (Activation) (None, 256) 0
|
||||
dropout_2 (Dropout) (None, 256) 0
|
||||
dense_3 (Dense) (None, 10) 2750
|
||||
activation_3 (Activation) (None, 10) 0
|
||||
=================================================================
|
||||
Total params: 269,322
|
||||
Trainable params: 269,322
|
||||
Non-trainable params: 0
|
||||
```
|
||||
|
||||
验证网络的另一种方法是通过调用:
|
||||
|
||||
```py
|
||||
plot_model(model, to_file='mlp-mnist.png', show_shapes=True)
|
||||
```
|
||||
|
||||
“图 1.3.9”显示了该图。 您会发现这类似于`summary()`的结果,但是以图形方式显示了每个层的互连和 I/O。
|
||||
|
||||

|
||||
|
||||
图 1.3.9:MLP MNIST 数字分类器的图形描述
|
||||
|
||||
在总结了我们模型的之后,到此结束了我们对 MLP 的讨论。 在下一部分中,我们将基于 CNN 构建 MNIST 数字分类器模型。
|
||||
|
||||
# 4\. 卷积神经网络(CNN)
|
||||
|
||||
现在,我们将进入第二个人工神经网络 CNN。 在本节中,我们将解决相同的 MNIST 数字分类问题,但这一次使用 CNN。
|
||||
|
||||
“图 1.4.1”显示了我们将用于 MNIST 数字分类的 CNN 模型,而其实现在“列表 1.4.1”中进行了说明。 实现 CNN 模型将需要对先前模型进行一些更改。 现在,输入张量不再具有输入向量,而具有新尺寸(`height`,`width`,`channels`)或(`image_size`,`image_size`,`1`)=(`28`,`28` ,`1`)用于 MNIST 灰度图像。 需要调整训练和测试图像的大小以符合此输入形状要求。
|
||||
|
||||

|
||||
|
||||
图 1.4.1:用于 MNIST 数字分类的 CNN 模型
|
||||
|
||||
实现上图:
|
||||
|
||||
“列表 1.4.1”:`cnn-mnist-1.4.1.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.models import Sequential
|
||||
from tensorflow.keras.layers import Activation, Dense, Dropout
|
||||
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten
|
||||
from tensorflow.keras.utils import to_categorical, plot_model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
```
|
||||
|
||||
```py
|
||||
# load mnist dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# compute the number of labels
|
||||
num_labels = len(np.unique(y_train))
|
||||
```
|
||||
|
||||
```py
|
||||
# convert to one-hot vector
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# input image dimensions
|
||||
image_size = x_train.shape[1]
|
||||
# resize and normalize
|
||||
x_train = np.reshape(x_train,[-1, image_size, image_size, 1])
|
||||
x_test = np.reshape(x_test,[-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
# image is processed as is (square grayscale)
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 128
|
||||
kernel_size = 3
|
||||
pool_size = 2
|
||||
filters = 64
|
||||
dropout = 0.2
|
||||
```
|
||||
|
||||
```py
|
||||
# model is a stack of CNN-ReLU-MaxPooling
|
||||
model = Sequential()
|
||||
model.add(Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
input_shape=input_shape))
|
||||
model.add(MaxPooling2D(pool_size))
|
||||
model.add(Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu'))
|
||||
model.add(MaxPooling2D(pool_size))
|
||||
model.add(Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu'))
|
||||
model.add(Flatten())
|
||||
# dropout added as regularizer
|
||||
model.add(Dropout(dropout))
|
||||
# output layer is 10-dim one-hot vector
|
||||
model.add(Dense(num_labels))
|
||||
model.add(Activation('softmax'))
|
||||
model.summary()
|
||||
plot_model(model, to_file='cnn-mnist.png', show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# loss function for one-hot vector
|
||||
# use of adam optimizer
|
||||
# accuracy is good metric for classification tasks
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
# train the network
|
||||
model.fit(x_train, y_train, epochs=10, batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
_, acc = model.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=0)
|
||||
print("\nTest accuracy: %.1f%%" % (100.0 * acc))
|
||||
```
|
||||
|
||||
的主要更改是`Conv2D`层的使用。 `ReLU`激活函数已经是`Conv2D`的参数。 当模型中包含`batch normalization`层时,可以将`ReLU`函数作为`Activation`层使用。 `Batch normalization`用于深层 CNN,因此可以利用较大的学习率而不会引起训练过程中的不稳定。
|
||||
|
||||
## 卷积
|
||||
|
||||
如果在 MLP 模型中,单元数量表示密集层,则核表示 CNN 操作。 如图“图 1.4.2”所示,可以将核可视化为矩形补丁或窗口,该补丁或窗口从左到右,从上到下在整个图像中滑动。 此操作称为卷积。 它将输入图像转换成特征映射,该特征映射表示核从输入图像中学到的内容。 然后将特征映射转换为后续层中的另一个特征映射,依此类推。 每个`Conv2D`生成的特征映射的数量由`filters`参数控制。
|
||||
|
||||

|
||||
|
||||
图 1.4.2:3×3 核与 MNIST 数字图像卷积。
|
||||
|
||||
在步骤`t[n]`和`t[n + 1]`中显示了卷积,其中核向右移动了 1 个像素 。
|
||||
|
||||
卷积中涉及的计算显示在“图 1.4.3”中:
|
||||
|
||||

|
||||
|
||||
图 1.4.3:卷积运算显示如何计算特征映射的一个元素
|
||||
|
||||
为简单起见,显示了应用了`3×3`核的`3×3`输入图像(或输入特征映射)。 卷积后显示结果特征映射。 特征映射中一个元素的值被加阴影。 您会注意到,结果特征映射小于原始输入图像的,这是因为卷积仅在有效元素上执行。 核不能超出映像的边界。 如果输入的尺寸应与输出特征映射相同,则`Conv2D`接受选项`padding='same'`。 输入在其边界周围填充零,以在卷积后保持尺寸不变。
|
||||
|
||||
## 池化操作
|
||||
|
||||
最后的更改是添加了`MaxPooling2D`层以及参数`pool_size=2`。 `MaxPooling2D`压缩每个特征映射。 每个大小为`pool_size × pool_size`的补丁都减少为 1 个特征映射点。 该值等于补丁中的最大特征点值。 下图显示了`MaxPooling2D`的两个补丁:
|
||||
|
||||

|
||||
|
||||
图 1.4.4:`MaxPooling2D`操作。 为简单起见,输入特征映射为`4×4`,结果为`2×2`特征映射。
|
||||
|
||||
`MaxPooling2D`的意义在于特征映射尺寸的减小,这转化为感受野尺寸的增加。 例如,在`MaxPooling2D(2)`之后,2×2 核现在大约与`4×4`补丁卷积。 CNN 学会了针对不同接收场大小的一组新的特征映射。
|
||||
|
||||
还有其他合并和压缩方式。 例如,要使`MaxPooling2D(2)`的尺寸减少 50%,`AveragePooling2D(2)`会取一个补丁的平均值而不是找到最大值。 交叉卷积`Conv2D(strides=2,…)`在卷积过程中将跳过每两个像素,并且仍具有相同的 50% 缩小效果。 每种还原技术的有效性都有细微的差异。
|
||||
|
||||
在`Conv2D`和`MaxPooling2D`中,`pool_size`和`kernel`都可以是非正方形的。 在这些情况下,必须同时指定行和列的大小。 例如,`pool_ size = (1, 2)`和`kernel = (3, 5)`。
|
||||
|
||||
最后一个`MaxPooling2D`操作的输出是一堆特征映射。 `Flatten`的作用是,将特征映射的栈转换为适用于`Dropout`或`Dense`层的向量格式,类似于 MLP 模型输出层。
|
||||
|
||||
在下一部分中,我们将评估经过训练的 MNIST CNN 分类器模型的表现。
|
||||
|
||||
## 表现评估和模型摘要
|
||||
|
||||
如“列表 1.4.2”中所示,“列表 1.4.1”中的 CNN 模型在 80,226 处需要较少数量的参数,而使用 MLP 层时需要 269,322 个参数。 `conv2d_1`层具有 640 个参数,因为每个核具有`3×3 = 9`个参数,并且 64 个特征映射中的每一个都有一个核,一个偏置参数。 其他卷积层的参数数量可以类似的方式计算。
|
||||
|
||||
“列表 1.4.2”:CNN MNIST 数字分类器的摘要
|
||||
|
||||
```py
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
conv2d_1 (Conv2D) (None, 26, 26, 64) 640
|
||||
max_pooling2d_1 (MaxPooiling2) (None, 13, 13, 64) 0
|
||||
conv2d_2 (Conv2D) (None, 11, 11, 64) 36928
|
||||
max_pooling2d_2 (MaxPooiling2) (None, 5.5, 5, 64) 0
|
||||
conv2d_3 (Conv2D) (None, 3.3, 3, 64) 36928
|
||||
flatten_1 (Flatten) (None, 576) 0
|
||||
dropout_1 (Dropout) (None, 576) 0
|
||||
dense_1 (Dense) (None, 10) 5770
|
||||
activation_1 (Activation) (None, 10) 0
|
||||
===================================================================
|
||||
Total params: 80,266
|
||||
Trainable params: 80,266
|
||||
Non-trainable params: 0
|
||||
```
|
||||
|
||||
“图 1.4.5”:显示了 CNN MNIST 数字分类器的图形表示形式。
|
||||
|
||||

|
||||
|
||||
图 1.4.5:CNN MNIST 数字分类器的图形描述
|
||||
|
||||
“表 1.4.1”显示了 99.4% 的最大测试准确率,这对于使用带有`dropout=0.2`的 Adam 优化器的每层具有 64 个特征映射的 3 层网络可以实现。 CNN 比 MLP 具有更高的参数效率,并且具有更高的准确率。 同样,CNN 也适合从顺序数据,图像和视频中学习表示形式。
|
||||
|
||||
| **层** | **优化器** | **正则化函数** | **训练准确率(%)** | **测试准确率(%)** |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 64-64-64 | SGD | 丢弃(0.2) | 97.76 | 98.50 |
|
||||
| 64-64-64 | RMSprop | 丢弃(0.2) | 99.11 | 99.00 |
|
||||
| 64-64-64 | Adam | 丢弃(0.2) | 99.75 | 99.40 |
|
||||
| 64-64-64 | Adam | 丢弃(0.4) | 99.64 | 99.30 |
|
||||
|
||||
表 1.4.1:CNN MNIST 数字分类器的不同 CNN 网络配置和表现指标。
|
||||
|
||||
看了 CNN 并评估了训练好的模型之后,让我们看一下我们将在本章中讨论的最终核心网络:RNN。
|
||||
|
||||
# 5\. 循环神经网络(RNN)
|
||||
|
||||
现在,我们来看一下三个人工神经网络中的最后一个,即 RNN。
|
||||
|
||||
RNN 是网络的序列,适用于学习顺序数据的表示形式,例如**自然语言处理**(**NLP**)中的文本或仪器中的传感器数据流 。 尽管每个 MNIST 数据样本本质上都不是顺序的,但不难想象每个图像都可以解释为像素行或列的序列。 因此,基于 RNN 的模型可以将每个 MNIST 图像作为 28 个元素的输入向量序列进行处理,时间步长等于 28。下面的清单在“图 1.5.1”中显示了 RNN 模型的代码:
|
||||
|
||||

|
||||
|
||||
图 1.5.1:用于 MNIST 数字分类的 RNN 模型
|
||||
|
||||
“列表 1.5.1”:`rnn-mnist-1.5.1.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.models import Sequential
|
||||
from tensorflow.keras.layers import Dense, Activation, SimpleRNN
|
||||
from tensorflow.keras.utils import to_categorical, plot_model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
```
|
||||
|
||||
```py
|
||||
# load mnist dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# compute the number of labels
|
||||
num_labels = len(np.unique(y_train))
|
||||
```
|
||||
|
||||
```py
|
||||
# convert to one-hot vector
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# resize and normalize
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train,[-1, image_size, image_size])
|
||||
x_test = np.reshape(x_test,[-1, image_size, image_size])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size)
|
||||
batch_size = 128
|
||||
units = 256
|
||||
dropout = 0.2
|
||||
```
|
||||
|
||||
```py
|
||||
# model is RNN with 256 units, input is 28-dim vector 28 timesteps
|
||||
model = Sequential()
|
||||
model.add(SimpleRNN(units=units,
|
||||
dropout=dropout,
|
||||
input_shape=input_shape))
|
||||
model.add(Dense(num_labels))
|
||||
model.add(Activation('softmax'))
|
||||
model.summary()
|
||||
plot_model(model, to_file='rnn-mnist.png', show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# loss function for one-hot vector
|
||||
# use of sgd optimizer
|
||||
# accuracy is good metric for classification tasks
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='sgd',
|
||||
metrics=['accuracy'])
|
||||
# train the network
|
||||
model.fit(x_train, y_train, epochs=20, batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
_, acc = model.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=0)
|
||||
print("\nTest accuracy: %.1f%%" % (100.0 * acc))
|
||||
```
|
||||
|
||||
RNN 分类器与之前的两个模型之间有两个主要区别。 首先是`input_shape = (image_size, image_size)`,它实际上是`input_ shape = (timesteps, input_dim)`或时间步长的`input_dim`维向量序列。 其次是使用`SimpleRNN`层以`units=256`表示 RNN 单元。 `units`变量代表输出单元的数量。 如果 CNN 是通过输入特征映射上的核卷积来表征的,则 RNN 输出不仅是当前输入的函数,而且是先前输出或隐藏状态的函数。 由于前一个输出也是前一个输入的函数,因此当前输出也是前一个输出和输入的函数,依此类推。 Keras 中的`SimpleRNN`层是真实 RNN 的简化版本。 以下等式描述了`SimpleRNN`的输出:
|
||||
|
||||
 (Equation 1.5.1)
|
||||
|
||||
在此等式中,`b`是偏差,而`W`和`U`被称为循环核(先前输出的权重)和核(当前输入的权重) ), 分别。 下标`t`用于指示序列中的位置。 对于具有`units=256`的`SimpleRNN`层,参数总数为`256 + 256×256 + 256×28 = 72,960`,对应于`b`,`W`和个贡献。
|
||||
|
||||
下图显示了用于分类任务的`SimpleRNN`和 RNN 的图。 使`SimpleRNN`比 RNN 更简单的是缺少输出值`o[t] = Vh[t] + c`在计算`softmax`函数之前:
|
||||
|
||||

|
||||
|
||||
图 1.5.2:`SimpleRNN`和 RNN 图
|
||||
|
||||
与 MLP 或 CNN 相比,RNN 最初可能较难理解。 在 MLP 中,感知器是基本单元。 一旦了解了感知器的概念,MLP 就是感知器的网络。 在 CNN 中,核是一个补丁或窗口,可在特征映射中滑动以生成另一个特征映射。 在 RNN 中,最重要的是自环的概念。 实际上只有一个单元。
|
||||
|
||||
出现多个单元的错觉是因为每个时间步都有一个单元,但实际上,除非网络展开,否则它只是重复使用的同一单元。 RNN 的基础神经网络在单元之间共享。
|
||||
|
||||
“列表 1.5.2”中的摘要指示使用`SimpleRNN`需要较少数量的参数。
|
||||
|
||||
“列表 1.5.2”:RNN MNIST 数字分类器的摘要
|
||||
|
||||
```py
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
simple_rnn_1 (SimpleRNN) (None, 256) 72960
|
||||
dense_1 (Dense) (None, 10) 2570
|
||||
activation_1 (Activation) (None, 10) 36928
|
||||
=================================================================
|
||||
Total params: 75,530
|
||||
Trainable params: 75,530
|
||||
Non-trainable params: 0
|
||||
```
|
||||
|
||||
“图 1.5.3”显示了 RNN MNIST 数字分类器的图形描述。 该模型非常简洁:
|
||||
|
||||

|
||||
|
||||
图 1.5.3:RNN MNIST 数字分类器图形说明
|
||||
|
||||
“表 1.5.1”显示 SimpleRNN 在所呈现的网络中具有最低的准确率:
|
||||
|
||||
| **层** | **优化器** | **正则化函数** | **训练准确率(%)** | **测试准确率(%)** |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 256 | SGD | 丢弃(0.2) | 97.26 | 98.00 |
|
||||
| 256 | RMSprop | 丢弃(0.2) | 96.72 | 97.60 |
|
||||
| 256 | Adam | 丢弃(0.2) | 96.79 | 97.40 |
|
||||
| 512 | SGD | 丢弃(0.2) | 97.88 | 98.30 |
|
||||
|
||||
表 1.5.1:不同的`SimpleRNN`网络配置和表现指标
|
||||
|
||||
在许多深度神经网络中,更常使用 RNN 家族的其他成员。 例如,机器翻译和问答问题都使用了**长短期记忆**(**LSTM**)。 LSTM 解决了长期依赖或记住与当前输出相关的过去信息的问题。
|
||||
|
||||
与 RNN 或`SimpleRNN`不同,LSTM 单元的内部结构更为复杂。“图 1.5.4”显示了 LSTM 的示意图。 LSTM 不仅使用当前输入和过去的输出或隐藏状态,还引入了一个单元状态`s[t]`,该状态将信息从一个单元传送到另一个单元。 单元状态之间的信息流由三个门控制`f[t]`,`i[t]`和`q[t]`。 这三个门的作用是确定应保留或替换哪些信息,以及过去对当前单元状态或输出有贡献的信息量以及过去和当前的输入。 我们不会在本书中讨论 LSTM 单元内部结构的细节。 但是,可以在[这个页面](http://colah.github.io/posts/2015-08-Understanding-LSTMs)上找到 LSTM 的直观指南。
|
||||
|
||||
`LSTM()`层可以用作`SimpleRNN()`的嵌入式替代。 如果 LSTM 对于手头的任务过于苛刻,则可以使用更简单的版本,称为**门控循环单元**(**GRU**)。 GRU 通过将单元状态和隐藏状态组合在一起来简化 LSTM。 GRU 还将门数量减少了一个。 `GRU()`函数也可以用作`SimpleRNN()`的直接替代品。
|
||||
|
||||

|
||||
|
||||
图 1.5.4:LSTM 图。为了清楚起见,未显示参数。
|
||||
|
||||
还有许多其他方法可以配置 RNN。 一种方法是制作双向 RNN 模型。 默认情况下,从当前输出仅受过去状态和当前输入影响的意义上讲,RNN 是单向的。
|
||||
|
||||
在双向 RNN 中,未来状态还可以通过允许信息向后流动来影响当前状态和过去状态。 根据收到的新信息,根据需要更新过去的输出。 可以通过调用包装器函数使 RNN 双向。 例如,双向 LSTM 的实现是`Bidirectional(LSTM())`。
|
||||
|
||||
对于所有类型的 RNN,增加单元数量也将增加容量。 但是,增加容量的另一种方法是堆叠 RNN 层。 尽管应注意,但作为一般经验法则,只有在需要时才应增加模型的容量。 容量过大可能会导致过拟合,结果可能导致训练时间延长和预测期间的表现降低。
|
||||
|
||||
# 6\. 总结
|
||||
|
||||
本章概述了三种深度学习模型(MLP,RNN,CNN),并介绍了 TensorFlow 2 `tf.keras`,这是一个用于快速开发,训练和测试适合于生产环境的深度学习模型的库。 还讨论了 Keras 的顺序 API。 在下一章中,将介绍函数式 API,这将使我们能够构建更复杂的模型,专门用于高级深度神经网络。
|
||||
|
||||
本章还回顾了深度学习的重要概念,例如优化,正则化和损失函数。 为了便于理解,这些概念是在 MNIST 数字分类的背景下提出的。
|
||||
|
||||
还讨论了使用人工神经网络(特别是 MLP,CNN 和 RNN)进行 MNIST 数字分类的不同解决方案,它们是深度神经网络的重要组成部分,并讨论了它们的表现指标。
|
||||
|
||||
了解了深度学习概念以及如何将 Keras 用作工具之后,我们现在可以分析高级深度学习模型。 在下一章讨论了函数式 API 之后,我们将继续执行流行的深度学习模型。 随后的章节将讨论选定的高级主题,例如自回归模型(自编码器,GAN,VAE),深度强化学习,对象检测和分段以及使用互信息的无监督学习。 随附的 Keras 代码实现将在理解这些主题方面发挥重要作用。
|
||||
|
||||
# 7\. 参考
|
||||
|
||||
1. `Chollet, François. Keras (2015). https://github.com/keras-team/keras.`
|
||||
2. `LeCun, Yann, Corinna Cortes, and C. J. Burges. MNIST handwritten digit database. AT&T Labs [Online]. Available: http://yann.lecun.com/exdb/mnist2 (2010).`
|
||||
@@ -1,897 +0,0 @@
|
||||
# 二、深度神经网络
|
||||
|
||||
在本章中,我们将研究深度神经网络。 这些网络在更具挑战性的数据集,如 ImageNet,[CIFAR10](https://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf) 和 CIFAR100。 为简洁起见,我们仅关注两个网络: **ResNet** [2] [4]和 **DenseNet** [5]。 尽管我们会更加详细,但重要的是花一点时间介绍这些网络。
|
||||
|
||||
ResNet 引入了残差学习的概念,使残障学习能够通过解决深度卷积网络中消失的梯度问题(在第 2 节中讨论)来构建非常深的网络。
|
||||
|
||||
DenseNet 允许每个卷积直接访问输入和较低层的特征映射,从而进一步改进了 ResNet。 通过利用**瓶颈**和**过渡层**,还可以在深层网络中将参数的数量保持为较低。
|
||||
|
||||
但是,为什么这些是两个模型,而不是其他? 好吧,自从引入它们以来,已经有无数的模型,例如 **ResNeXt** [6]和 **WideResNet** [7],它们受到这两个网络使用的技术的启发。 同样,在了解 ResNet 和 DenseNet 的情况下,我们将能够使用他们的设计指南来构建我们自己的模型。 通过使用迁移学习,这也将使我们能够将预训练的 ResNet 和 DenseNet 模型用于我们自己的目的,例如对象检测和分割。 仅出于这些原因,以及与 Keras 的兼容性,这两个模型非常适合探索和补充本书的高级深度学习范围。
|
||||
|
||||
尽管本章的重点是深度神经网络; 在本章中,我们将讨论 Keras 的重要功能,称为**函数式 API**。 该 API 充当在`tf.keras`中构建网络的替代方法,使我们能够构建更复杂的网络,而这是顺序模型 API 无法实现的。 我们之所以专注于此 API 的原因是,它将成为构建诸如本章重点介绍的两个之类的深度网络的非常有用的工具。 建议您先完成“第 1 章”,“Keras 的高级深度学习介绍”,然后再继续本章,因为我们将参考在本章中探讨的入门级代码和概念,我们将它们带入了更高的层次。
|
||||
|
||||
本章的目的是介绍:
|
||||
|
||||
* Keras 中的函数式 API,以及探索运行该 API 的网络示例
|
||||
* `tf.keras`中的深度残差网络(ResNet 版本 1 和 2)实现
|
||||
* `tf.keras`中密集连接卷积网络(DenseNet)的实现
|
||||
* 探索两种流行的深度学习模型,即 **ResNet** 和 **DenseNet**
|
||||
|
||||
让我们开始讨论函数式 API。
|
||||
|
||||
# 1\. 函数式 API
|
||||
|
||||
在我们首先在“第 1 章”,“Keras 高级深度学习入门”的顺序模型 API 中,一层堆叠在另一层之上。 通常,将通过其输入和输出层访问模型。 我们还了解到,如果我们发现自己想要在网络中间添加辅助输入,或者甚至想在最后一层之前提取辅助输出,则没有简单的机制。
|
||||
|
||||
这种模式也有缺点。 例如,它不支持类似图的模型或行为类似于 Python 函数的模型。 此外,在两个模型之间共享层也很困难。函数式 API 解决了这些局限性,这就是为什么它对于想要使用深度学习模型的任何人来说都是至关重要的工具的原因。
|
||||
|
||||
函数式 API 遵循以下两个概念:
|
||||
|
||||
* 层是接受张量作为参数的实例。 一层的输出是另一个张量。 为了构建模型,层实例是通过输入和输出张量彼此链接的对象。 这与在顺序模型中堆叠多个层有类似的最终结果。 但是,使用层实例会使模型更容易具有辅助或多个输入和输出,因为每个层的输入/输出将易于访问。
|
||||
* 模型是一个或多个输入张量和输出张量之间的函数。 在模型输入和输出之间,张量是通过层输入和输出张量彼此链接的层实例。 因此,模型是一个或多个输入层和一个或多个输出层的函数。 该模型实例将数据从输入流到输出流的形式的计算图形式化。
|
||||
|
||||
在完成函数式 API 模型的构建之后,训练和评估将由顺序模型中使用的相同函数执行。 为了说明,在函数式 API 中,二维卷积层`Conv2D`带有 32 个过滤器,并且`x`作为层输入张量,`y`作为层输出张量可以写为:
|
||||
|
||||
```py
|
||||
y = Conv2D(32)(x)
|
||||
```
|
||||
|
||||
我们也可以堆叠多层来构建模型。 例如,我们可以使用函数式 API 重写 MNIST `cnn-mnist-1.4.1.py`上的**卷积神经网络**(**CNN**),如下所示:
|
||||
|
||||
“列表 2.1.1”:`cnn-functional-2.1.1.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.layers import Dense, Dropout, Input
|
||||
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten
|
||||
from tensorflow.keras.models import Model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
from tensorflow.keras.utils import to_categorical
|
||||
```
|
||||
|
||||
```py
|
||||
# load MNIST dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# from sparse label to categorical
|
||||
num_labels = len(np.unique(y_train))
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape and normalize input images
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train,[-1, image_size, image_size, 1])
|
||||
x_test = np.reshape(x_test,[-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 128
|
||||
kernel_size = 3
|
||||
filters = 64
|
||||
dropout = 0.3
|
||||
```
|
||||
|
||||
```py
|
||||
# use functional API to build cnn layers
|
||||
inputs = Input(shape=input_shape)
|
||||
y = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu')(inputs)
|
||||
y = MaxPooling2D()(y)
|
||||
y = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu')(y)
|
||||
y = MaxPooling2D()(y)
|
||||
y = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu')(y)
|
||||
# image to vector before connecting to dense layer
|
||||
y = Flatten()(y)
|
||||
# dropout regularization
|
||||
y = Dropout(dropout)(y)
|
||||
outputs = Dense(num_labels, activation='softmax')(y)
|
||||
```
|
||||
|
||||
```py
|
||||
# build the model by supplying inputs/outputs
|
||||
model = Model(inputs=inputs, outputs=outputs)
|
||||
# network model in text
|
||||
model.summary()
|
||||
# classifier loss, Adam optimizer, classifier accuracy
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
```
|
||||
|
||||
```py
|
||||
# train the model with input images and labels
|
||||
model.fit(x_train,
|
||||
y_train,
|
||||
validation_data=(x_test, y_test),
|
||||
epochs=20,
|
||||
batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
# model accuracy on test dataset
|
||||
score = model.evaluate(x_test,
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=0)
|
||||
print("\nTest accuracy: %.1f%%" % (100.0 * score[1]))
|
||||
```
|
||||
|
||||
默认情况下,使用`pool_size=2`作为参数,因此`MaxPooling2D`已被删除。
|
||||
|
||||
在前面的清单中,每一层都是张量的函数。 每一层生成一个张量作为输出,该张量成为下一层的输入。 要创建此模型,我们可以调用`Model()`并提供`inputs`和`outputs`张量,或者提供张量列表。 其他一切保持不变。
|
||||
|
||||
类似于顺序模型,也可以使用`fit()`和`evaluate()`函数来训练和评估相同的列表。 实际上,`Sequential`类是`Model`类的子类。 我们需要记住,我们在`fit()`函数中插入了`validation_data`参数,以查看训练期间验证准确率的进度。 在 20 个周期内,准确率范围从 99.3% 到 99.4%。
|
||||
|
||||
## 创建两输入一输出模型
|
||||
|
||||
现在,我们将做一些令人兴奋的事情,创建一个具有两个输入和一个输出的高级模型。 在开始之前,重要的是要知道序列模型 API 是为仅构建 1 输入和 1 输出模型而设计的。
|
||||
|
||||
假设发明了一种用于 MNIST 数字分类的新模型,它称为 Y 网络,如图“图 2.1.1”所示。 Y 网络在左 CNN 分支和右 CNN 分支两次使用相同的输入。 网络使用`concatenate`层合并结果。 合并操作`concatenate`类似于沿连接轴堆叠两个相同形状的张量以形成一个张量。 例如,沿着最后一个轴连接两个形状为`(3, 3, 16)`的张量将导致一个形状为`(3, 3, 32)`的张量。
|
||||
|
||||
`concatenate`层之后的所有其他内容将与上一章的 CNN MNIST 分类器模型相同:`Flatten`,然后是`Dropout`,然后是`Dense`:
|
||||
|
||||

|
||||
|
||||
图 2.1.1:Y 网络接受两次相同的输入,但是在卷积网络的两个分支中处理输入。 分支的输出使用连接层进行合并。最后一层的预测将类似于上一章的 CNN MNIST 分类器模型。
|
||||
|
||||
为了提高“列表 2.1.1”中模型的表现,我们可以提出一些更改。 首先,Y 网络的分支将过滤器数量加倍,以补偿`MaxPooling2D()`之后特征映射尺寸的减半。 例如,如果第一个卷积的输出为`(28, 28, 32)`,则在最大池化之后,新形状为`(14, 14, 32)`。 下一个卷积的过滤器大小为 64,输出尺寸为`(14, 14, 64)`。
|
||||
|
||||
其次,尽管两个分支的核大小相同,但右分支使用 2 的扩展率。“图 2.1.2”显示了不同的扩展率对大小为 3 的核的影响。 这个想法是,通过使用扩张率增加核的有效接受域大小,CNN 将使正确的分支能够学习不同的特征映射。 使用大于 1 的扩张速率是一种计算有效的近似方法,可以增加接收场的大小。 这是近似值,因为该核实际上不是成熟的核。 这是有效的,因为我们使用与膨胀率等于 1 相同的操作数。
|
||||
|
||||
要了解接受域的概念,请注意,当核计算特征映射的每个点时,其输入是前一层特征映射中的补丁,该补丁也取决于其前一层特征映射。 如果我们继续将此依赖关系一直跟踪到输入图像,则核将依赖于称为接收场的图像补丁。
|
||||
|
||||
我们将使用选项`padding='same'`来确保使用扩张的 CNN 时不会出现负张量。 通过使用`padding='same'`,我们将使输入的尺寸与输出特征映射相同。 这是通过用零填充输入以确保输出的**大小**相同来实现的。
|
||||
|
||||

|
||||
|
||||
图 2.1.2:通过从 1 增加膨胀率,有效的核接受域大小也增加了
|
||||
|
||||
“列表 2.1.2”的`cnn-y-network-2.1.2.py`显示了使用函数式 API 的 Y 网络的实现。 两个分支由两个`for`循环创建。 两个分支期望输入形状相同。 两个`for`循环将创建两个`Conv2D-Dropout-MaxPooling2D`的三层栈。 虽然我们使用`concatenate`层组合了左右分支的输出,但我们还可以利用`tf.keras`的其他合并函数,例如`add`,`dot`和`multiply`。 合并函数的选择并非纯粹是任意的,而必须基于合理的模型设计决策。
|
||||
|
||||
在 Y 网络中,`concatenate`不会丢弃特征映射的任何部分。 取而代之的是,我们让`Dense`层确定如何处理连接的特征映射。
|
||||
|
||||
“列表 2.1.2”:`cnn-y-network-2.1.2.py`
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
from tensorflow.keras.layers import Dense, Dropout, Input
|
||||
from tensorflow.keras.layers import Conv2D, MaxPooling2D
|
||||
from tensorflow.keras.layers import Flatten, concatenate
|
||||
from tensorflow.keras.models import Model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
from tensorflow.keras.utils import to_categorical
|
||||
from tensorflow.keras.utils import plot_model
|
||||
```
|
||||
|
||||
```py
|
||||
# load MNIST dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# from sparse label to categorical
|
||||
num_labels = len(np.unique(y_train))
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape and normalize input images
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train,[-1, image_size, image_size, 1])
|
||||
x_test = np.reshape(x_test,[-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 32
|
||||
kernel_size = 3
|
||||
dropout = 0.4
|
||||
n_filters = 32
|
||||
```
|
||||
|
||||
```py
|
||||
# left branch of Y network
|
||||
left_inputs = Input(shape=input_shape)
|
||||
x = left_inputs
|
||||
filters = n_filters
|
||||
# 3 layers of Conv2D-Dropout-MaxPooling2D
|
||||
# number of filters doubles after each layer (32-64-128)
|
||||
for i in range(3):
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
padding='same',
|
||||
activation='relu')(x)
|
||||
x = Dropout(dropout)(x)
|
||||
x = MaxPooling2D()(x)
|
||||
filters *= 2
|
||||
```
|
||||
|
||||
```py
|
||||
# right branch of Y network
|
||||
right_inputs = Input(shape=input_shape)
|
||||
y = right_inputs
|
||||
filters = n_filters
|
||||
# 3 layers of Conv2D-Dropout-MaxPooling2Do
|
||||
# number of filters doubles after each layer (32-64-128)
|
||||
for i in range(3):
|
||||
y = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
padding='same',
|
||||
activation='relu',
|
||||
dilation_rate=2)(y)
|
||||
y = Dropout(dropout)(y)
|
||||
y = MaxPooling2D()(y)
|
||||
filters *= 2
|
||||
```
|
||||
|
||||
```py
|
||||
# merge left and right branches outputs
|
||||
y = concatenate([x, y])
|
||||
# feature maps to vector before connecting to Dense
|
||||
y = Flatten()(y)
|
||||
y = Dropout(dropout)(y)
|
||||
outputs = Dense(num_labels, activation='softmax')(y)
|
||||
```
|
||||
|
||||
```py
|
||||
# build the model in functional API
|
||||
model = Model([left_inputs, right_inputs], outputs)
|
||||
# verify the model using graph
|
||||
plot_model(model, to_file='cnn-y-network.png', show_shapes=True)
|
||||
# verify the model using layer text description
|
||||
model.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# classifier loss, Adam optimizer, classifier accuracy
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer='adam',
|
||||
metrics=['accuracy'])
|
||||
```
|
||||
|
||||
```py
|
||||
# train the model with input images and labels
|
||||
model.fit([x_train, x_train],
|
||||
y_train,
|
||||
validation_data=([x_test, x_test], y_test),
|
||||
epochs=20,
|
||||
batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
# model accuracy on test dataset
|
||||
score = model.evaluate([x_test, x_test],
|
||||
y_test,
|
||||
batch_size=batch_size,
|
||||
verbose=0)
|
||||
print("\nTest accuracy: %.1f%%" % (100.0 * score[1]))
|
||||
```
|
||||
|
||||
退后一步,我们可以注意到 Y 网络期望有两个输入用于训练和验证。 输入是相同的,因此提供了`[x_train, x_train]`。
|
||||
|
||||
在 20 个周期的过程中,Y 网络的准确率为 99.4% 至 99.5%。 与 3 叠 CNN 相比,这是一个微小的改进,CNN 的精度在 99.3% 到 99.4% 之间。 但是,这是以更高的复杂度和两倍以上的参数数量为代价的。
|
||||
|
||||
下图“图 2.1.3”显示了 Keras 理解并由`plot_model()`函数生成的 Y 网络的架构:
|
||||
|
||||

|
||||
|
||||
图 2.1.3:清单 2.1.2 中实现的 CNN Y 网络
|
||||
|
||||
总结我们对函数式 API 的了解。 我们应该花时间记住本章的重点是构建深度神经网络,特别是 ResNet 和 DenseNet。 因此,我们只讨论构建它们所需的函数式 API 材料,因为涵盖整个的 API 将超出本书的范围。 话虽如此,让我们继续讨论 ResNet。
|
||||
|
||||
有关函数式 API 的其他信息,请阅读[这里](https://keras.io/)。
|
||||
|
||||
# 2\. 深度残差网络(ResNet)
|
||||
|
||||
深度网络的一个主要优点是,它们具有从输入图和特征映射学习不同级别表示的能力。 在分类,分割,检测和许多其他计算机视觉问题中,学习不同的特征映射通常可以提高性能。
|
||||
|
||||
但是,您会发现训练深层网络并不容易,因为在反向传播过程中,梯度可能会随着浅层中的深度消失(或爆炸)。“图 2.2.1”说明了梯度消失的问题。 通过从输出层向所有先前层的反向传播来更新网络参数。 由于反向传播是基于链法则的,因此当梯度到达浅层时,梯度会逐渐减小。 这是由于小数的乘法,尤其是对于小损失函数和参数值。
|
||||
|
||||
乘法运算的数量将与网络深度成正比。 还要注意的是,如果梯度降低,则不会适当更新参数。
|
||||
|
||||
因此,网络将无法提高其表现。
|
||||
|
||||

|
||||
|
||||
图 2.2.1:深层网络中的一个常见问题是,在反向传播过程中,梯度在到达浅层时会消失。
|
||||
|
||||
为了减轻深度网络中梯度的降级,ResNet 引入了深度残差学习框架的概念。 让我们分析一个块:深度网络的一小部分。
|
||||
|
||||
“图 2.2.2”显示了典型 CNN 块和 ResNet 残差块之间的比较。 ResNet 的想法是,为了防止梯度降级,我们将让信息通过快捷连接流到浅层。
|
||||
|
||||

|
||||
|
||||
图 2.2.2:典型 CNN 中的块与 ResNet 中的块之间的比较。 为了防止反向传播期间梯度的降低,引入了快捷连接。
|
||||
|
||||
接下来,我们将在中讨论两个模块之间的差异,以了解更多详细信息。“图 2.2.3”显示了另一个常用的深层网络 **VGG** [3]和 ResNet 的 CNN 块的更多详细信息。 我们将层特征映射表示为`x`。 层`l`的特征映射为`x[l]`。 在 CNN 层中的操作是 **Conv2D 批量规范化(BN)- ReLU**。
|
||||
|
||||
假设我们以`H() = Conv2D-Batch Normalization(BN)-ReLU`的形式表示这组操作; 然后:
|
||||
|
||||
`x[l-1] = H(x[l-2])`(公式 2.2.1)
|
||||
|
||||
`x[l] = H(x[l-1])`(方程式 2.2.2)
|
||||
|
||||
换句话说,通过`H() =Conv2D-Batch Normalization(BN)-ReLU`将`l-2`层上的特征映射转换为`x[l-1]`。 应用相同的操作集将`x[l-1]`转换为`x[l]`。 换句话说,如果我们有一个 18 层的 VGG,则在将输入图像转换为第 18 个层特征映射之前,有 18 个`H()`操作。
|
||||
|
||||
一般而言,我们可以观察到`l`层输出特征映射仅直接受先前的特征映射影响。 同时,对于 ResNet:
|
||||
|
||||
`x[l-1] = H(x[l-2])`(公式 2.2.3)
|
||||
|
||||
`x[l] = ReLU(F(x[l-1]) + x[l-2])`(公式 2.2.4)
|
||||
|
||||

|
||||
|
||||
图 2.2.3:普通 CNN 块和残差块的详细层操作
|
||||
|
||||
`F(x[l-1])`由`Conv2D-BN`制成,这也被称为残差映射。 `+`符号是快捷方式连接和`F(x[l-1])`输出之间的张量元素加法。 快捷连接不会增加额外的参数,也不会增加计算复杂度。
|
||||
|
||||
可以通过`add()`合并函数在`tf.keras`中实现添加操作。 但是,`F(x[l-1])`和`x[l-2]`应该具有相同的尺寸。
|
||||
|
||||
如果尺寸不同,例如,当更改特征映射尺寸时,我们应该在`x[l-2]`上进行线性投影以匹配尺寸`F([l-1])`的含量。 在原始论文中,当特征映射的大小减半时,情况的线性投影是通过`Conv2D`和 1 `strides=2`核完成的。
|
||||
|
||||
在“第 1 章”,“Keras 高级深度学习”,我们讨论了`stride > 1`等效于在卷积期间跳过像素。 例如,如果`strides=2`,则在卷积过程中滑动核时,可以跳过其他每个像素。
|
||||
|
||||
前面的“公式 2.2.3”和“公式 2.2.4”都对 ResNet 残余块操作进行建模。 他们暗示,如果可以训练较深的层具有较少的误差,则没有理由为什么较浅的层应具有较高的误差。
|
||||
|
||||
知道 ResNet 的基本构建块后,我们就可以设计一个深度残差网络来进行图像分类。 但是,这一次,我们将处理更具挑战性的数据集。
|
||||
|
||||
在我们的示例中,我们将考虑 CIFAR10,它是原始论文所基于的数据集之一。 在此示例中,`tf.keras`提供了一个 API,可以方便地访问 CIFAR10 数据集,如下所示:
|
||||
|
||||
```py
|
||||
from tensorflow.keras.datasets import cifar10
|
||||
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
|
||||
```
|
||||
|
||||
与 MNIST 一样,CIFAR10 数据集也有 10 个类别。 数据集是对应于飞机,汽车,鸟,猫,鹿,狗,青蛙,马,船和卡车的小型(`32×32`)RGB 真实世界图像的集合。 10 个类别中的每个类别。“图 2.2.4”显示了来自 CIFAR10 的示例图像。
|
||||
|
||||
在数据集中,有 50,000 个标记的训练图像和 10,000 个标记的测试图像用于验证:
|
||||
|
||||

|
||||
|
||||
图 2.2.4:来自 CIFAR10 数据集的样本图像。 完整的数据集包含 50,000 张标签的训练图像和 10,000 张标签的测试图像以进行验证。
|
||||
|
||||
对于 CIFAR10 数据,可以使用“表 2.2.1”中所示的不同网络架构来构建 ResNet。“表 2.2.1”表示我们有三组残差块。 每组具有对应于`n`个残余块的`2n`层。`32×32`的额外层是输入图像的第一层。
|
||||
|
||||
| **层** | **输出大小** | **过滤器尺寸** | **操作** |
|
||||
| --- | --- | --- | --- |
|
||||
| 卷积 | `32 × 32` | 16 | `3 x 3 Conv2D` |
|
||||
| 残差块(1) | `32 × 32` | |  |
|
||||
| 过渡层(1) | `32 × 32` | | `{1 x 1 Conv2D, stride = 2}` |
|
||||
| | `16 × 16` | |
|
||||
| 残差块(2) | `16 × 16` | 32 |  |
|
||||
| 过渡层(2) | `16 × 16` | | | | `{1 x 1 Conv2D, stride = 2}` |
|
||||
| | `8 × 8` | |
|
||||
| 残差块(3) | `8 × 8` | 64 |  |
|
||||
| 平均池化 | `1 × 1` | | | `8 x 8 AveragePooling2D` |
|
||||
|
||||
表 2.2.1:ResNet 网络架构配置
|
||||
|
||||
核大小为 3,不同大小的两个特征映射之间的过渡除外,该过渡实现了线性映射。 例如,核大小为 1 的`Conv2D`和`strides=2`。 为了与 DenseNet 保持一致,当我们连接两个大小不同的剩余块时,我们将使用项`Transition`层。
|
||||
|
||||
ResNet 使用`kernel_initializer='he_normal'`以便在进行反向传播时帮助收敛[1]。 最后一层由`AveragePooling2D-Flatten-Dense`制成。 在这一点上值得注意的是 ResNet 不使用丢弃。 似乎`add` 合并操作和`1 x 1`卷积具有自正则化效果。“图 2.2.5”显示了 CIFAR10 数据集的 ResNet 模型架构,如“表 2.2.1”中所述。
|
||||
|
||||

|
||||
|
||||
图 2.2.5:用于 CIFAR10 数据集分类的 ResNet 的模型架构
|
||||
|
||||
以下代码段显示了`tf.keras`中的部分 ResNet 实现。 该代码已添加到 Keras GitHub 存储库中。 从“表 2.2.2”(稍后显示)中,我们还可以看到,通过修改`n`的值,我们可以增加网络的深度。
|
||||
|
||||
例如,对于`n = 18`,我们已经拥有 ResNet110,这是一个具有 110 层的深度网络。 要构建 ResNet20,我们使用`n = 3`:
|
||||
|
||||
```py
|
||||
n = 3
|
||||
```
|
||||
|
||||
```py
|
||||
# model version
|
||||
# orig paper: version = 1 (ResNet v1),
|
||||
# improved ResNet: version = 2 (ResNet v2)
|
||||
version = 1
|
||||
```
|
||||
|
||||
```py
|
||||
# computed depth from supplied model parameter n
|
||||
if version == 1:
|
||||
depth = n * 6 + 2
|
||||
elif version == 2:
|
||||
depth = n * 9 + 2
|
||||
```
|
||||
|
||||
```py
|
||||
if version == 2:
|
||||
model = resnet_v2(input_shape=input_shape, depth=depth)
|
||||
else:
|
||||
model = resnet_v1(input_shape=input_shape, depth=depth)
|
||||
```
|
||||
|
||||
`resnet_v1()`方法是 ResNet 的模型构建器。 它使用工具函数`resnet_layer(),`来帮助构建`Conv2D-BN-ReLU`的栈。
|
||||
|
||||
它将称为版本 1,正如我们将在下一节中看到的那样,提出了一种改进的 ResNet,该版本称为 ResNet 版本 2 或 v2。 通过 ResNet,ResNet v2 改进了残差块设计,从而提高了表现。
|
||||
|
||||
以下清单显示了`resnet-cifar10-2.2.1.py`的部分代码,它是 ResNet v1 的`tf.keras`模型实现。
|
||||
|
||||
“列表 2.2.1”:`resnet-cifar10-2.2.1.py`
|
||||
|
||||
```py
|
||||
def resnet_v1(input_shape, depth, num_classes=10):
|
||||
"""ResNet Version 1 Model builder [a]
|
||||
```
|
||||
|
||||
```py
|
||||
Stacks of 2 x (3 x 3) Conv2D-BN-ReLU
|
||||
Last ReLU is after the shortcut connection.
|
||||
At the beginning of each stage, the feature map size is halved
|
||||
(downsampled) by a convolutional layer with strides=2, while
|
||||
the number of filters is doubled. Within each stage,
|
||||
the layers have the same number filters and the
|
||||
same number of filters.
|
||||
Features maps sizes:
|
||||
stage 0: 32x32, 16
|
||||
stage 1: 16x16, 32
|
||||
stage 2: 8x8, 64
|
||||
The Number of parameters is approx the same as Table 6 of [a]:
|
||||
ResNet20 0.27M
|
||||
ResNet32 0.46M
|
||||
ResNet44 0.66M
|
||||
ResNet56 0.85M
|
||||
ResNet110 1.7M
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
input_shape (tensor): shape of input image tensor
|
||||
depth (int): number of core convolutional layers
|
||||
num_classes (int): number of classes (CIFAR10 has 10)
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
model (Model): Keras model instance
|
||||
"""
|
||||
if (depth - 2) % 6 != 0:
|
||||
raise ValueError('depth should be 6n+2 (eg 20, 32, in [a])')
|
||||
# Start model definition.
|
||||
num_filters = 16
|
||||
num_res_blocks = int((depth - 2) / 6)
|
||||
```
|
||||
|
||||
```py
|
||||
inputs = Input(shape=input_shape)
|
||||
x = resnet_layer(inputs=inputs)
|
||||
# instantiate the stack of residual units
|
||||
for stack in range(3):
|
||||
for res_block in range(num_res_blocks):
|
||||
strides = 1
|
||||
# first layer but not first stack
|
||||
if stack > 0 and res_block == 0:
|
||||
strides = 2 # downsample
|
||||
y = resnet_layer(inputs=x,
|
||||
num_filters=num_filters,
|
||||
strides=strides)
|
||||
y = resnet_layer(inputs=y,
|
||||
num_filters=num_filters,
|
||||
activation=None)
|
||||
# first layer but not first stack
|
||||
if stack > 0 and res_block == 0:
|
||||
# linear projection residual shortcut
|
||||
# connection to match changed dims
|
||||
x = resnet_layer(inputs=x,
|
||||
num_filters=num_filters,
|
||||
kernel_size=1,
|
||||
strides=strides,
|
||||
activation=None,
|
||||
batch_normalization=False)
|
||||
x = add([x, y])
|
||||
x = Activation('relu')(x)
|
||||
num_filters *= 2
|
||||
```
|
||||
|
||||
```py
|
||||
# add classifier on top.
|
||||
# v1 does not use BN after last shortcut connection-ReLU
|
||||
x = AveragePooling2D(pool_size=8)(x)
|
||||
y = Flatten()(x)
|
||||
outputs = Dense(num_classes,
|
||||
activation='softmax',
|
||||
kernel_initializer='he_normal')(y)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate model.
|
||||
model = Model(inputs=inputs, outputs=outputs)
|
||||
return model
|
||||
```
|
||||
|
||||
ResNet 在`n`的各种值上的表现显示在“表 2.2.2”中。
|
||||
|
||||
| **层** | `n` | **CIFAR10 的准确率百分比(原始论文)** | **CIFAR10 的准确率百分比(本书)** |
|
||||
| --- | --- | --- | --- |
|
||||
| ResNet20 | 3 | 91.25 | 92.16 |
|
||||
| ResNet32 | 5 | 92.49 | 92.46 |
|
||||
| ResNet44 | 7 | 92.83 | 92.50 |
|
||||
| ResNet56 | 9 | 93.03 | 92.71 |
|
||||
| ResNet110 | 18 | 93.57 | 92.65 |
|
||||
|
||||
表 2.2.2:针对不同的 n 值,使用 CIFAR10 验证的 ResNet 架构
|
||||
|
||||
与 ResNet 的原始实现有一些细微的差异。 特别是,我们不使用 SGD,而是使用 Adam。 这是因为 ResNet 更容易与 Adam 融合。 我们还将使用学习率调度器`lr_schedule()`,以便将`lr`的减少量从默认的`1e-3`缩短为 80、120、160 和 180 个周期。 在训练期间的每个周期之后,都会将`lr_schedule()`函数作为回调变量的一部分进行调用。
|
||||
|
||||
每当验证准确率方面取得进展时,另一个回调将保存检查点。 训练深层网络时,保存模型或权重检查点是一个好习惯。 这是因为训练深度网络需要大量时间。
|
||||
|
||||
当您想使用网络时,您只需要做的就是重新加载检查点,然后恢复经过训练的模型。 这可以通过调用`tf.keras load_model()`来完成。 包含`lr_reducer()`函数。 如果指标在排定的减少之前已稳定在上,则如果在`patience = 5`周期之后验证损失没有改善,则此回调将以参数中提供的某个因子来降低学习率。
|
||||
|
||||
调用`model.fit()`方法时,会提供**回调**变量。 与原始论文相似,`tf.keras`实现使用数据扩充`ImageDataGenerator()`来提供其他训练数据作为正则化方案的一部分。 随着训练数据数量的增加,概括性将会提高。
|
||||
|
||||
例如,简单的数据扩充就是翻转一条狗的照片,如图“图 2.2.6”(`horizontal_flip = True`)所示。 如果它是狗的图像,则翻转的图像仍然是狗的图像。 您还可以执行其他变换,例如缩放,旋转,变白等等,并且标签将保持不变:
|
||||
|
||||

|
||||
|
||||
图 2.2.6:一个简单的数据扩充就是翻转原始图像
|
||||
|
||||
[完整的代码可在 GitHub 上获得](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
准确复制原始论文的实现通常很困难。 在本书中,我们使用了不同的优化器和数据扩充。 这可能会导致本书中所实现的`tf.keras` ResNet 和原始模型中的表现略有不同。
|
||||
|
||||
在 **ResNet** [4]的第二篇论文发布之后,本节中介绍的原始模型为,称为 ResNet v1。 改进的 ResNet 通常称为 ResNet v2,我们将在下一部分讨论。
|
||||
|
||||
# 3\. ResNet v2
|
||||
|
||||
ResNet v2 的改进主要体现在残块中各层的排列中,如图“图 2.3.1”所示。
|
||||
|
||||
ResNet v2 的主要变化是:
|
||||
|
||||
* 使用`1 x 1 – 3 x 3 – 1 × 1`的栈`BN-ReLU-Conv2D`
|
||||
* 批量标准化和 ReLU 激活先于二维卷积
|
||||
|
||||

|
||||
|
||||
图 2.3.1:ResNet v1 和 ResNet v2 之间的剩余块比较
|
||||
|
||||
ResNet v2 也以与`resnet-cifar10-2.2.1.py`相同的代码实现,如“列表 2.2.1”所示:
|
||||
|
||||
“列表 2.2.1”:`resnet-cifar10-2.2.1.py`
|
||||
|
||||
```py
|
||||
def resnet_v2(input_shape, depth, num_classes=10):
|
||||
"""ResNet Version 2 Model builder [b]
|
||||
```
|
||||
|
||||
```py
|
||||
Stacks of (1 x 1)-(3 x 3)-(1 x 1) BN-ReLU-Conv2D or
|
||||
also known as bottleneck layer.
|
||||
First shortcut connection per layer is 1 x 1 Conv2D.
|
||||
Second and onwards shortcut connection is identity.
|
||||
At the beginning of each stage,
|
||||
the feature map size is halved (downsampled)
|
||||
by a convolutional layer with strides=2,
|
||||
while the number of filter maps is
|
||||
doubled. Within each stage, the layers have
|
||||
the same number filters and the same filter map sizes.
|
||||
Features maps sizes:
|
||||
conv1 : 32x32, 16
|
||||
stage 0: 32x32, 64
|
||||
stage 1: 16x16, 128
|
||||
stage 2: 8x8, 256
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
input_shape (tensor): shape of input image tensor
|
||||
depth (int): number of core convolutional layers
|
||||
num_classes (int): number of classes (CIFAR10 has 10)
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
model (Model): Keras model instance
|
||||
"""
|
||||
if (depth - 2) % 9 != 0:
|
||||
raise ValueError('depth should be 9n+2 (eg 110 in [b])')
|
||||
# start model definition.
|
||||
num_filters_in = 16
|
||||
num_res_blocks = int((depth - 2) / 9)
|
||||
```
|
||||
|
||||
```py
|
||||
inputs = Input(shape=input_shape)
|
||||
# v2 performs Conv2D with BN-ReLU
|
||||
# on input before splitting into 2 paths
|
||||
x = resnet_layer(inputs=inputs,
|
||||
num_filters=num_filters_in,
|
||||
conv_first=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate the stack of residual units
|
||||
for stage in range(3):
|
||||
for res_block in range(num_res_blocks):
|
||||
activation = 'relu'
|
||||
batch_normalization = True
|
||||
strides = 1
|
||||
if stage == 0:
|
||||
num_filters_out = num_filters_in * 4
|
||||
# first layer and first stage
|
||||
if res_block == 0:
|
||||
activation = None
|
||||
batch_normalization = False
|
||||
else:
|
||||
num_filters_out = num_filters_in * 2
|
||||
# first layer but not first stage
|
||||
if res_block == 0:
|
||||
# downsample
|
||||
strides = 2
|
||||
```
|
||||
|
||||
```py
|
||||
# bottleneck residual unit
|
||||
y = resnet_layer(inputs=x,
|
||||
num_filters=num_filters_in,
|
||||
kernel_size=1,
|
||||
strides=strides,
|
||||
activation=activation,
|
||||
batch_normalization=batch_normalization,
|
||||
conv_first=False)
|
||||
y = resnet_layer(inputs=y,
|
||||
num_filters=num_filters_in,
|
||||
conv_first=False)
|
||||
y = resnet_layer(inputs=y,
|
||||
num_filters=num_filters_out,
|
||||
kernel_size=1,
|
||||
conv_first=False)
|
||||
if res_block == 0:
|
||||
# linear projection residual shortcut connection
|
||||
# to match changed dims
|
||||
x = resnet_layer(inputs=x,
|
||||
num_filters=num_filters_out,
|
||||
kernel_size=1,
|
||||
strides=strides,
|
||||
activation=None,
|
||||
batch_normalization=False)
|
||||
x = add([x, y])
|
||||
```
|
||||
|
||||
```py
|
||||
num_filters_in = num_filters_out
|
||||
```
|
||||
|
||||
```py
|
||||
# add classifier on top.
|
||||
# v2 has BN-ReLU before Pooling
|
||||
x = BatchNormalization()(x)
|
||||
x = Activation('relu')(x)
|
||||
x = AveragePooling2D(pool_size=8)(x)
|
||||
y = Flatten()(x)
|
||||
outputs = Dense(num_classes,
|
||||
activation='softmax',
|
||||
kernel_initializer='he_normal')(y)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate model.
|
||||
model = Model(inputs=inputs, outputs=outputs)
|
||||
return model
|
||||
```
|
||||
|
||||
下面的代码显示了 ResNet v2 的模型构建器。 例如,要构建 ResNet110 v2,我们将使用`n = 12`和`version = 2`:
|
||||
|
||||
```py
|
||||
n = 12
|
||||
```
|
||||
|
||||
```py
|
||||
# model version
|
||||
# orig paper: version = 1 (ResNet v1),
|
||||
# improved ResNet: version = 2 (ResNet v2)
|
||||
version = 2
|
||||
```
|
||||
|
||||
```py
|
||||
# computed depth from supplied model parameter n
|
||||
if version == 1:
|
||||
depth = n * 6 + 2
|
||||
elif version == 2:
|
||||
depth = n * 9 + 2
|
||||
```
|
||||
|
||||
```py
|
||||
if version == 2:
|
||||
model = resnet_v2(input_shape=input_shape, depth=depth)
|
||||
else:
|
||||
model = resnet_v1(input_shape=input_shape, depth=depth)
|
||||
```
|
||||
|
||||
ResNet v2 的准确率显示在下面的“表 2.3.1”中:
|
||||
|
||||
| **层** | `n` | **CIFAR10 的准确率百分比(原始论文)** | **CIFAR10 的准确率百分比(本书)** |
|
||||
| --- | --- | --- | --- |
|
||||
| ResNet56 | 9 | 不适用 | 93.01 |
|
||||
| ResNet110 | 18 | 93.63 | 93.15 |
|
||||
|
||||
表 2.3.1:在 CIFAR10 数据集上验证的 ResNet v2 架构
|
||||
|
||||
在 Keras 应用包中,已实现某些 ResNet v1 和 v2 模型(例如:50、101、152)。 这些是替代的实现方式,其中预训练的权重不清楚,可以轻松地重新用于迁移学习。 本书中使用的模型在层数方面提供了灵活性。
|
||||
|
||||
我们已经完成了对最常用的深度神经网络之一 ResNet v1 和 v2 的讨论。 在以下部分中,将介绍另一种流行的深度神经网络架构 DenseNet。
|
||||
|
||||
# 4\. 紧密连接的卷积网络(DenseNet)
|
||||
|
||||

|
||||
|
||||
图 2.4.1:DenseNet 中的一个 4 层`Dense`块,每层的输入均由所有先前的特征映射组成。
|
||||
|
||||
DenseNet 使用另一种方法攻击梯度消失的问题。 代替使用快捷方式连接,所有先前的特征映射都将成为下一层的输入。 上图显示了一个`Dense`块中密集互连的示例。
|
||||
|
||||
为简单起见,在此图中,我们仅显示四层。 注意,层`l`的输入是所有先前特征映射的连接。 如果用操作`H`表示`BN-ReLU-Conv2D`(`x`),则层`l`的输出为:
|
||||
|
||||
`x[l] = H(x[0], x[1], x[2], x[l-1])`(公式 2.4.1)
|
||||
|
||||
`Conv2D`使用大小为 3 的核。每层生成的特征映射的数量称为增长率`k`。 通常,在 Huang 等人的论文“密集连接卷积网络”中,也使用`k = 12`,但是`k = 24` [5]。 因此,如果特征映射`x[0]`的数量为`k[0]`,则“图 2.4.1”中,4 层`Dense`块的末尾的特征映射总数为`4 x k + k[0]`。
|
||||
|
||||
DenseNet 建议在`Dense`块之前加上`BN-ReLU-Conv2D`,以及许多是增长率两倍的特征映射`k[0]`= 2 x`k`。 在`Dense`块的末尾,特征映射的总数将为`4 x 12 + 2 x 12 = 72`。
|
||||
|
||||
在输出层,DenseNet 建议我们在具有`softmax`层的`Dense()`之前执行平均池化。 如果未使用数据扩充,则必须在`Dense`块`Conv2D`之后跟随一个丢弃层。
|
||||
|
||||
随着网络的深入,将出现两个新问题。 首先,由于每一层都贡献了`k`特征映射,因此`l`层的输入数量为`(l – 1) x k + k[0]`。 特征映射可以在深层中快速增长,从而减慢了计算速度。 例如,对于 101 层网络,对于`k = 12`,这将是`1200 + 24 = 1224`。
|
||||
|
||||
其次,类似于 ResNet,随着网络的不断深入,特征映射的大小将减小,从而增加核的接收域大小。 如果 DenseNet 在合并操作中使用连接,则必须协调大小上的差异。
|
||||
|
||||
为了防止特征映射的数量增加到计算效率低的程度,DenseNet 引入了`Bottleneck`层,如图“图 2.4.2”所示。 这个想法是,在每次连接之后,现在应用`1 x 1`卷积,其过滤器大小等于`4k`。 这种降维技术阻止了`Conv2D(3)`处理的特征映射的数量快速增加。
|
||||
|
||||

|
||||
|
||||
图 2.4.2:DenseNet 的 Dense 块中的一层,带有和不带有瓶颈层 BN-ReLU-Conv2D(1)。 为了清楚起见,我们将核大小作为 Conv2D 的参数。
|
||||
|
||||
然后`Bottleneck`层将 DenseNet 层修改为`BN-ReLU-Conv2D(1)-BN- ReLU-Conv2D(3)`,而不仅仅是`BN-ReLU-Conv2D(3)`。 为了清楚起见,我们将核大小作为`Conv2D`的参数。 在瓶颈层,每个`Conv2D(3)`仅处理 4 个`k`特征映射,而不是`(l – 1) x k + k[0]`的,对于层`l`。 例如,对于 101 层网络,最后一个`Conv2D(3)`的输入仍然是`k = 12`而不是先前计算的 1224 的 48 个特征映射。
|
||||
|
||||
为了解决特征映射大小不匹配的问题,DenseNet 将深度网络划分为多个 Dense 块,这些块通过过渡层连接在一起,如图“图 2.4.3”所示。 在每个`Dense`块中,特征映射的大小(即宽度和高度)将保持不变。
|
||||
|
||||
过渡层的作用是在两个`Dense`块之间从一个特征映射大小过渡到较小的特征映射大小。 尺寸通常减少一半。 这是通过平均池化层完成的。 例如,默认值为`pool_size=2`的`AveragePooling2D`会将大小从`(64, 64, 256)`减小为`(32, 32, 256)`。 过渡层的输入是前一个`Dense`块中最后一个连接层的输出。
|
||||
|
||||

|
||||
|
||||
图 2.4.3:两个密集块之间的过渡层
|
||||
|
||||
但是,在将特征映射传递到平均池之前,使用`Conv2D(1)`将其数量减少某个压缩因子`0 < θ < 1`。DenseNet 在实验中使用`θ = 0.5`。 例如,如果先前`Dense`块的最后连接的输出是`(64, 64, 512)`,则在`Conv2D(1)`之后,特征映射的新尺寸将是`(64, 64, 256)`。 当压缩和降维放在一起时,过渡层由`BN-Conv2D(1)-AveragePooling2D`层组成。 实际上,批量归一化在卷积层之前。
|
||||
|
||||
现在,我们已经涵盖了 DenseNet 的重要概念。 接下来,我们将为`tf.keras`中的 CIFAR10 数据集构建并验证 DenseNet-BC。
|
||||
|
||||
## 为 CIFAR10 构建 100 层 DenseNet-BC
|
||||
|
||||
现在,我们将要为 CIFAR10 数据集构建一个具有 100 层的 **DenseNet-BC**(**瓶颈压缩**), 我们在上面讨论过。
|
||||
|
||||
“表 2.4.1”显示了模型配置,而“图 2.4.4”显示了模型架构。 清单为我们展示了具有 100 层的 DenseNet-BC 的部分 Keras 实现。 我们需要注意的是,我们使用`RMSprop`,因为在使用 DenseNet 时,它的收敛性优于 SGD 或 Adam。
|
||||
|
||||
| **层** | **输出大小** | **DenseNet-100 BC** |
|
||||
| --- | --- | --- |
|
||||
| 卷积 | `32 x 32` | `3 x 3 Conv2D` |
|
||||
| 密集块(1) | `32 x 32` |  |
|
||||
| 过渡层(1) | `32 x 32` |  |
|
||||
| `16 x 16` |
|
||||
| 密集块(2) | `16 x 16` |  |
|
||||
| 过渡层(2) | `16 x 16` |  |
|
||||
| `8 x 8` |
|
||||
| 密集块(3) | `8 x 8` |  |
|
||||
| 平均池化 | `1 x 1` | `8 x 8 AveragePooling2D` |
|
||||
| 分类层 | | `Flatten-Dense(10)-softmax` |
|
||||
|
||||
表 2.4.1:100 层的 DenseNet-BC 用于 CIFAR10 分类
|
||||
|
||||
将从配置移至架构:
|
||||
|
||||

|
||||
|
||||
图 2.4.4:用于 CIFAR10 分类的 100 个层的 DenseNet-BC 模型架构
|
||||
|
||||
下面“列表 2.4.1”是具有 100 层的 DenseNet-BC 的部分 Keras 实现,如“表 2.4.1”所示。
|
||||
|
||||
“列表 2.4.1”:`densenet-cifar10-2.4.1.py`
|
||||
|
||||
```py
|
||||
# start model definition
|
||||
# densenet CNNs (composite function) are made of BN-ReLU-Conv2D
|
||||
inputs = Input(shape=input_shape)
|
||||
x = BatchNormalization()(inputs)
|
||||
x = Activation('relu')(x)
|
||||
x = Conv2D(num_filters_bef_dense_block,
|
||||
kernel_size=3,
|
||||
padding='same',
|
||||
kernel_initializer='he_normal')(x)
|
||||
x = concatenate([inputs, x])
|
||||
```
|
||||
|
||||
```py
|
||||
# stack of dense blocks bridged by transition layers
|
||||
for i in range(num_dense_blocks):
|
||||
# a dense block is a stack of bottleneck layers
|
||||
for j in range(num_bottleneck_layers):
|
||||
y = BatchNormalization()(x)
|
||||
y = Activation('relu')(y)
|
||||
y = Conv2D(4 * growth_rate,
|
||||
kernel_size=1,
|
||||
padding='same',
|
||||
kernel_initializer='he_normal')(y)
|
||||
if not data_augmentation:
|
||||
y = Dropout(0.2)(y)
|
||||
y = BatchNormalization()(y)
|
||||
y = Activation('relu')(y)
|
||||
y = Conv2D(growth_rate,
|
||||
kernel_size=3,
|
||||
padding='same',
|
||||
kernel_initializer='he_normal')(y)
|
||||
if not data_augmentation:
|
||||
y = Dropout(0.2)(y)
|
||||
x = concatenate([x, y])
|
||||
```
|
||||
|
||||
```py
|
||||
# no transition layer after the last dense block
|
||||
if i == num_dense_blocks - 1:
|
||||
continue
|
||||
# transition layer compresses num of feature maps and # reduces the size by 2
|
||||
num_filters_bef_dense_block += num_bottleneck_layers * growth_rate
|
||||
num_filters_bef_dense_block = int(num_filters_bef_dense_block * compression_factor)
|
||||
y = BatchNormalization()(x)
|
||||
y = Conv2D(num_filters_bef_dense_block,
|
||||
kernel_size=1,
|
||||
padding='same',
|
||||
kernel_initializer='he_normal')(y)
|
||||
if not data_augmentation:
|
||||
y = Dropout(0.2)(y)
|
||||
x = AveragePooling2D()(y)
|
||||
```
|
||||
|
||||
```py
|
||||
# add classifier on top
|
||||
# after average pooling, size of feature map is 1 x 1
|
||||
x = AveragePooling2D(pool_size=8)(x)
|
||||
y = Flatten()(x)
|
||||
outputs = Dense(num_classes,
|
||||
kernel_initializer='he_normal',
|
||||
activation='softmax')(y)
|
||||
# instantiate and compile model
|
||||
# orig paper uses SGD but RMSprop works better for DenseNet
|
||||
model = Model(inputs=inputs, outputs=outputs)
|
||||
model.compile(loss='categorical_crossentropy',
|
||||
optimizer=RMSprop(1e-3),
|
||||
metrics=['accuracy'])
|
||||
model.summary()
|
||||
```
|
||||
|
||||
训练 DenseNet 的`tf.keras`实现 200 个周期,可以达到 93.74% 的准确率,而本文中报道的是 95.49%。 使用数据扩充。 我们在 ResNet v1 / v2 中为 DenseNet 使用了相同的回调函数。
|
||||
|
||||
对于更深的层,必须使用 Python 代码上的表来更改`growth_rate`和`depth`变量。 但是,如本文所述,以深度 190 或 250 训练网络将需要大量时间。 为了给我们一个训练时间的想法,每个周期在 1060Ti GPU 上运行大约一个小时。 与 ResNet 相似,Keras 应用包具有针对 DenseNet 121 及更高版本的预训练模型。
|
||||
|
||||
DenseNet 完成了我们对深度神经网络的讨论。 与 ResNet 一起,这两个网络已成为许多下游任务中不可或缺的特征提取器网络。
|
||||
|
||||
# 5\. 总结
|
||||
|
||||
在本章中,我们介绍了函数式 API 作为使用`tf.keras`构建复杂的深度神经网络模型的高级方法。 我们还演示了如何使用函数式 API 来构建多输入单输出 Y 网络。 与单分支 CNN 网络相比,该网络具有更高的准确率。 在本书的其余部分中,我们将发现在构建更复杂和更高级的模型时必不可少的函数式 API。 例如,在下一章中,函数式 API 将使我们能够构建模块化编码器,解码器和自编码器。
|
||||
|
||||
我们还花费了大量时间探索两个重要的深度网络 ResNet 和 DenseNet。 这两个网络不仅用于分类,而且还用于其他领域,例如分段,检测,跟踪,生成和视觉语义理解。 在“第 11 章”,“对象检测”和“第 12 章”,“语义分割”中,我们将使用 ResNet 进行对象检测和分割。 我们需要记住,与仅仅遵循原始实现相比,更仔细地了解 ResNet 和 DenseNet 中的模型设计决策至关重要。 这样,我们就可以将 ResNet 和 DenseNet 的关键概念用于我们的目的。
|
||||
|
||||
# 6\. 参考
|
||||
|
||||
1. `Kaiming He et al. Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification. Proceedings of the IEEE international conference on computer vision, 2015 (https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/He_Delving_Deep_into_ICCV_2015_paper.pdfspm=5176.100239.blogcont55892.28.pm8zm1&file=He_Delving_Deep_into_ICCV_2015_paper.pdf).`
|
||||
1. `Kaiming He et al. Deep Residual Learning for Image Recognition. Proceedings of the IEEE conference on computer vision and pattern recognition, 2016a (http://openaccess.thecvf.com/content_cvpr_2016/papers/He_Deep_Residual_Learning_CVPR_2016_paper.pdf).`
|
||||
1. `Karen Simonyan and Andrew Zisserman. Very Deep Convolutional Networks for Large-Scale Image Recognition. ICLR, 2015 (https://arxiv.org/pdf/1409.1556/).`
|
||||
1. `Kaiming He et al. Identity Mappings in Deep Residual Networks. European Conference on Computer Vision. Springer International Publishing, 2016b (https://arxiv.org/pdf/1603.05027.pdf).`
|
||||
1. `Gao Huang et al. Densely Connected Convolutional Networks. Proceedings of the IEEE conference on computer vision and pattern recognition, 2017 (http://openaccess.thecvf.com/content_cvpr_2017/papers/Huang_Densely_Connected_Convolutional_CVPR_2017_paper.pdf).`
|
||||
1. `Saining Xie et al. Aggregated Residual Transformations for Deep Neural Networks. Computer Vision and Pattern Recognition (CVPR), 2017 IEEE Conference on. IEEE, 2017 (http://openaccess.thecvf.com/content_cvpr_2017/papers/Xie_Aggregated_Residual_Transformations_CVPR_2017_paper.pdf).`
|
||||
1. `Zagoruyko, Sergey, and Nikos Komodakis. "Wide residual networks." arXiv preprint arXiv:1605.07146 (2016).`
|
||||
@@ -1,891 +0,0 @@
|
||||
# 三、自编码器
|
||||
|
||||
在上一章“第 2 章”,“深度神经网络”中,我们介绍了深度神经网络的概念。 现在,我们将继续研究自编码器,它是一种神经网络架构,试图找到给定输入数据的压缩表示形式。
|
||||
|
||||
与前面的章节相似,输入数据可以采用多种形式,包括语音,文本,图像或视频。 自编码器将尝试查找表示形式或一段代码,以便对输入数据执行有用的转换。 例如,当对自编码器进行降噪处理时,神经网络将尝试找到可用于将噪声数据转换为干净数据的代码。 嘈杂的数据可以是带有静态噪声的录音形式,然后将其转换为清晰的声音。 自编码器将自动从数据中自动学习代码,而无需人工标记。 这样,自编码器可以在**无监督**学习算法下分类为。
|
||||
|
||||
在本书的后续章节中,我们将研究**生成对抗网络**(**GAN**)和**变分自编码器**(**VAE**) 也是无监督学习算法的代表形式。 这与我们在前几章中讨论过的监督学习算法相反,后者需要人工标注。
|
||||
|
||||
总之,本章介绍:
|
||||
|
||||
* 自编码器的原理
|
||||
* 如何使用`tf.keras`实现自编码器
|
||||
* 去噪和着色自编码器的实际应用
|
||||
|
||||
让我们从了解自编码器是什么以及自编码器的原理开始。
|
||||
|
||||
# 1\. 自编码器的原理
|
||||
|
||||
自编码器以最简单的形式通过尝试将输入复制到输出中来学习表示形式或代码。 但是,使用自编码器并不像将输入复制到输出那样简单。 否则,神经网络将无法发现输入分布中的隐藏结构。
|
||||
|
||||
自编码器将输入分布编码为低维张量,通常采用向量形式。 这将近似通常称为潜在表示,代码或向量的隐藏结构。 该处理构成编码部分。 然后,潜在向量将由解码器部分解码,以恢复原始输入。
|
||||
|
||||
由于潜向量是输入分布的低维压缩表示,因此应该期望解码器恢复的输出只能近似输入。 输入和输出之间的差异可以通过损失函数来衡量。
|
||||
|
||||
但是为什么我们要使用自编码器? 简而言之,自编码器在原始形式或更复杂的神经网络的一部分中都有实际应用。
|
||||
|
||||
它们是了解深度学习的高级主题的关键工具,因为它们为我们提供了适合密度估计的低维数据表示。 此外,可以有效地对其进行处理以对输入数据执行结构化操作。 常见的操作包括去噪,着色,特征级算术,检测,跟踪和分割,仅举几例。
|
||||
|
||||
在本节中,我们将介绍自编码器的原理。 我们将使用前几章介绍的带有 MNIST 数据集的自编码器。
|
||||
|
||||
首先,我们需要意识到自编码器具有两个运算符,它们是:
|
||||
|
||||
* **编码器**:这会将输入`x`转换为低维潜向量`z = f(x)`。 由于潜向量是低维的,编码器被迫仅学习输入数据的最重要特征。 例如,在 MNIST 数字的情况下,要学习的重要特征可能包括书写风格,倾斜角度,笔触圆度,厚度等。 从本质上讲,这些是代表数字 0 至 9 所需的最重要的信息位。
|
||||
* **解码器**:这尝试从潜在向量`g(z) = x`中恢复输入。
|
||||
|
||||
尽管潜向量的维数较小,但它的大小足以使解码器恢复输入数据。
|
||||
|
||||
解码器的目标是使`x_tilde`尽可能接近`x`。 通常,编码器和解码器都是非线性函数。`z`的尺寸是可以表示的重要特征数量的度量。 该维数通常比输入维数小得多,以提高效率,并为了限制潜在代码仅学习输入分布的最显着属性[1]。
|
||||
|
||||
当潜码的维数明显大于`x`时,自编码器倾向于记忆输入。
|
||||
|
||||
合适的损失函数`L(x, x_tilde)`衡量输入`x`与输出(即)恢复后的输入`x_tilde`的相异程度。 如下式所示,均方误差(MSE)是此类损失函数的一个示例:
|
||||
|
||||
 (Equation 3.1.1)
|
||||
|
||||
在此示例中,`m`是输出尺寸(例如,在 MNIST 中,`m = width × height × channels = 28 × 28 × 1 = 784`)。`x[i]`和`x_tilde[i]`分别是`x`和`x_tilde`的元素。 由于损失函数是输入和输出之间差异的量度,因此我们可以使用替代的重建损失函数,例如二进制交叉熵或结构相似性指数(SSIM)。
|
||||
|
||||
与其他神经网络类似,自编码器会在训练过程中尝试使此误差或损失函数尽可能小。“图 3.1.1”显示了一个自编码器。 编码器是将输入`x`压缩为低维潜向量`z`的函数。 该潜向量代表输入分布的重要特征。 然后,解码器尝试以`x_tilde`的形式从潜向量中恢复原始输入。
|
||||
|
||||

|
||||
|
||||
图 3.1.1:自编码器的框图
|
||||
|
||||
为了将自编码器置于上下文中,`x`可以是尺寸为`28×28×1 = 784`的 MNIST 数字。编码器将输入转换为低维的`z`,可以是 16 维潜在向量。 解码器将尝试从`z`中以`x_tilde`的形式恢复输入。
|
||||
|
||||
在视觉上,每个 MNIST 数字`x`看起来都类似于`x_tilde`。“图 3.1.2”向我们演示了此自编码过程。
|
||||
|
||||

|
||||
|
||||
图 3.1.2:带有 MNIST 数字输入和输出的自编码器。 潜在向量为 16 角
|
||||
|
||||
我们可以看到,虽然解码后的数字 7 并不完全相同,但仍然足够接近。
|
||||
|
||||
由于编码器和解码器都是非线性函数,因此我们可以使用神经网络来实现两者。 例如,在 MNIST 数据集中,自编码器可以由 MLP 或 CNN 实现。 通过最小化通过反向传播的损失函数,可以训练自编码器。 与其他神经网络类似,反向传播的要求是损失函数必须是可微的。
|
||||
|
||||
如果将输入视为分布,则可以将编码器解释为分布的编码器,`p(z | x)`,将解码器解释为分布的解码器`p(x | z)`。 自编码器的损失函数表示为:
|
||||
|
||||
 (Equation 3.1.2)
|
||||
|
||||
损失函数只是意味着我们要在给定潜在向量分布的情况下最大程度地恢复输入分布的机会。 如果假设解码器的输出分布为为高斯,则损失函数归结为 MSE,因为:
|
||||
|
||||
 (Equation 3.1.3)
|
||||
|
||||
在此示例中,`N(x[i]; x_tilde[i], σ²`表示平均值为`x_tilde[i]`且方差为`σ²`的高斯分布。 假设恒定方差。 假定解码器输出`x_tilde[i]`是独立的。`m`是输出尺寸。
|
||||
|
||||
了解自编码器背后的原理将有助于我们执行代码。 在下一节中,我们将研究如何使用`tf.keras`函数式 API 来构建编码器,解码器和自编码器。
|
||||
|
||||
# 2\. 使用 Keras 构建自编码器
|
||||
|
||||
现在,我们要使用进行一些令人兴奋的事情,使用`tf.keras`库构建一个自编码器。 为了简单起见,我们将使用 MNIST 数据集作为第一组示例。 然后,自编码器将根据输入数据生成潜向量,并使用解码器恢复输入。 在该第一示例中,潜向量是 16 维。
|
||||
|
||||
首先,我们将通过构建编码器来实现自编码器。
|
||||
|
||||
“列表 3.2.1”显示了将 MNIST 数字压缩为 16 维潜在向量的编码器。 编码器是两个`Conv2D`的栈。 最后阶段是具有 16 个单元的`Dense`层,以生成潜向量。
|
||||
|
||||
“列表 3.2.1”:`autoencoder-mnist-3.2.1.py`
|
||||
|
||||
```py
|
||||
from tensorflow.keras.layers import Dense, Input
|
||||
from tensorflow.keras.layers import Conv2D, Flatten
|
||||
from tensorflow.keras.layers import Reshape, Conv2DTranspose
|
||||
from tensorflow.keras.models import Model
|
||||
from tensorflow.keras.datasets import mnist
|
||||
from tensorflow.keras.utils import plot_model
|
||||
from tensorflow.keras import backend as K
|
||||
```
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
```
|
||||
|
||||
```py
|
||||
# load MNIST dataset
|
||||
(x_train, _), (x_test, _) = mnist.load_data()
|
||||
# reshape to (28, 28, 1) and normalize input images
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
|
||||
x_test = np.reshape(x_test, [-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 32
|
||||
kernel_size = 3
|
||||
latent_dim = 16
|
||||
# encoder/decoder number of CNN layers and filters per layer
|
||||
layer_filters = [32, 64]
|
||||
# build the autoencoder model
|
||||
# first build the encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
x = inputs
|
||||
# stack of Conv2D(32)-Conv2D(64)
|
||||
for filters in layer_filters:
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# shape info needed to build decoder model
|
||||
# so we don't do hand computation
|
||||
# the input to the decoder's first
|
||||
# Conv2DTranspose will have this shape
|
||||
# shape is (7, 7, 64) which is processed by
|
||||
# the decoder back to (28, 28, 1)
|
||||
shape = K.int_shape(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate latent vector
|
||||
x = Flatten()(x)
|
||||
latent = Dense(latent_dim, name='latent_vector')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model(inputs,
|
||||
latent,
|
||||
name='encoder')
|
||||
encoder.summary()
|
||||
plot_model(encoder,
|
||||
to_file='encoder.png',
|
||||
show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# build the decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='decoder_input')
|
||||
# use the shape (7, 7, 64) that was earlier saved
|
||||
x = Dense(shape[1] * shape[2] * shape[3])(latent_inputs)
|
||||
# from vector to suitable shape for transposed conv
|
||||
x = Reshape((shape[1], shape[2], shape[3]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# stack of Conv2DTranspose(64)-Conv2DTranspose(32)
|
||||
for filters in layer_filters[::-1]:
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# reconstruct the input
|
||||
outputs = Conv2DTranspose(filters=1,
|
||||
kernel_size=kernel_size,
|
||||
activation='sigmoid',
|
||||
padding='same',
|
||||
name='decoder_output')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model(latent_inputs, outputs, name='decoder')
|
||||
decoder.summary()
|
||||
plot_model(decoder, to_file='decoder.png', show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# autoencoder = encoder + decoder
|
||||
# instantiate autoencoder model
|
||||
autoencoder = Model(inputs,
|
||||
decoder(encoder(inputs)),
|
||||
name='autoencoder')
|
||||
autoencoder.summary()
|
||||
plot_model(autoencoder,
|
||||
to_file='autoencoder.png',
|
||||
show_shapes=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# Mean Square Error (MSE) loss function, Adam optimizer
|
||||
autoencoder.compile(loss='mse', optimizer='adam')
|
||||
```
|
||||
|
||||
```py
|
||||
# train the autoencoder
|
||||
autoencoder.fit(x_train,
|
||||
x_train,
|
||||
validation_data=(x_test, x_test),
|
||||
epochs=1,
|
||||
batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
# predict the autoencoder output from test data
|
||||
x_decoded = autoencoder.predict(x_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# display the 1st 8 test input and decoded images
|
||||
imgs = np.concatenate([x_test[:8], x_decoded[:8]])
|
||||
imgs = imgs.reshape((4, 4, image_size, image_size))
|
||||
imgs = np.vstack([np.hstack(i) for i in imgs])
|
||||
plt.figure()
|
||||
plt.axis('off')
|
||||
plt.title('Input: 1st 2 rows, Decoded: last 2 rows')
|
||||
plt.imshow(imgs, interpolation='none', cmap='gray')
|
||||
plt.savefig('input_and_decoded.png')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
“图 3.2.1”显示了`plot_model()`生成的架构模型图,与`encoder.summary()`生成的文本版本相同。 保存最后一个`Conv2D`的输出形状以计算解码器输入层的尺寸,以便轻松重建 MNIST 图像:`shape = K.int_shape(x)`。
|
||||
|
||||

|
||||
|
||||
图 3.2.1:编码器模型由`Conv2D(32) - Conv2D(64) - Dense(16)`组成,以生成低维潜向量
|
||||
|
||||
列表 3.2.1 中的解码器对潜在向量进行解压缩,以恢复 MNIST 数字。 解码器输入级是`Dense`层,它将接受潜在向量。 单元的数量等于从编码器保存的`Conv2D`输出尺寸的乘积。 这样做是为了便于我们调整`Dense`层`Dense`层的输出大小,以最终恢复原始 MNIST 图像尺寸。
|
||||
|
||||
解码器由三个`Conv2DTranspose`的栈组成。 在我们的案例中,我们将使用**转置的 CNN**(有时称为**反卷积**),它是解码器中常用的。 我们可以将转置的 CNN(`Conv2DTranspose`)想象成 CNN 的逆过程。
|
||||
|
||||
在一个简单的示例中,如果 CNN 将图像转换为特征映射,则转置的 CNN 将生成给定特征映射的图像。“图 3.2.2”显示了解码器模型:
|
||||
|
||||

|
||||
|
||||
图 3.2.2:解码器模型由`Dense(16) - Conv2DTranspose(64) - Conv2DTranspose(32) - Conv2DTranspose(1)`组成。 输入是经过解码以恢复原始输入的潜向量
|
||||
|
||||
通过将编码器和解码器连接在一起,我们可以构建自编码器。“图 3.2.3”说明了自编码器的模型图:
|
||||
|
||||

|
||||
|
||||
图 3.2.3:通过将编码器模型和解码器模型结合在一起来构建自编码器模型。 此自编码器有 178 k 个参数
|
||||
|
||||
编码器的张量输出也是解码器的输入,该解码器生成自编码器的输出。 在此示例中,我们将使用 MSE 损失函数和 Adam 优化器。 在训练期间,输入与输出`x_train`相同。 我们应该注意,在我们的示例中,只有几层足以将验证损失在一个周期内驱动到 0.01。 对于更复杂的数据集,我们可能需要更深的编码器和解码器,以及更多的训练时间。
|
||||
|
||||
在对自编码器进行了一个周期的验证损失为 0.01 的训练之后,我们能够验证它是否可以对以前从未见过的 MNIST 数据进行编码和解码。“图 3.2.4”向我们展示了来自测试数据和相应解码图像的八个样本:
|
||||
|
||||

|
||||
|
||||
图 3.2.4:根据测试数据预测自编码器。 前两行是原始输入测试数据。 最后两行是预测数据
|
||||
|
||||
除了图像中的轻微模糊之外,我们能够轻松识别出自编码器能够以良好的质量恢复输入。 随着我们训练更多的周期,结果将有所改善。
|
||||
|
||||
在这一点上,我们可能想知道:我们如何可视化空间中的潜在向量? 一种简单的可视化方法是强制自编码器使用 2 维潜在向量来学习 MNIST 数字特征。 从那里,我们可以将该潜在向量投影到二维空间上,以查看 MNIST 潜在向量的分布方式。“图 3.2.5”和“图 3.2.6”显示了 MNIST 数字的分布与潜在代码尺寸的关系。
|
||||
|
||||

|
||||
|
||||
图 3.2.5:MNIST 数字分布与潜在代码尺寸`z[0]`和`z[1]`的关系。 原始照片可以在本书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter3-autoencoders/README.md)中找到。
|
||||
|
||||
在“图 3.2.5”中,我们可以看到特定数字的潜向量聚集在空间的某个区域上。 例如,数字 0 在左下象限中,而数字 1 在右上象限中。 这种群集在图中得到了反映。 实际上,同一图显示了导航或从潜在空间生成新数字的结果,如图“图 3.2.5”所示。
|
||||
|
||||
例如,从中心开始,向右上象限改变 2 维潜向量的值,这表明数字从 9 变为 1。这是可以预期的,因为从“图 3.2.5”开始,我们可以看到数字 9 群集的潜在代码值在中心附近,数字 1 群集的潜在代码值在右上象限。
|
||||
|
||||
对于“图 3.2.5”和“图 3.2.6”,我们仅研究了每个潜在向量维在 -4.0 和 +4.0 之间的区域:
|
||||
|
||||

|
||||
|
||||
图 3.2.6:导航 2 维潜在向量空间时生成的数字
|
||||
|
||||
从“图 3.2.5”中可以看出,潜在代码分布不是连续的。 理想情况下,应该看起来像一个圆圈,其中到处都有有效值。 由于这种不连续性,因此如果解码潜伏向量,则几乎不会产生任何可识别的数字。
|
||||
|
||||
“图 3.2.5”和“图 3.2.6”经过 20 个训练周期后生成。 通过设置`latent_dim = 2`修改了`autoencoder-mnist-3.2.1.py`代码。 `plot_ results()`函数将 MNIST 数字绘制为 2 维潜在向量的函数。 为了方便起见,该程序另存为`autoencoder-2dim-mnist-3.2.2.py`,其部分代码显示在“列表 3.2.2”中。 其余代码实际上类似于“列表 3.2.1”,在此不再显示。
|
||||
|
||||
“列表 3.2.2”:`autoencoder-2dim-mnist-3.2.2.py`
|
||||
|
||||
```py
|
||||
def plot_results(models,
|
||||
data,
|
||||
batch_size=32,
|
||||
model_name="autoencoder_2dim"):
|
||||
"""Plots 2-dim latent values as scatter plot of digits
|
||||
then, plot MNIST digits as function of 2-dim latent vector
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
models (list): encoder and decoder models
|
||||
data (list): test data and label
|
||||
batch_size (int): prediction batch size
|
||||
model_name (string): which model is using this function
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
encoder, decoder = models
|
||||
x_test, y_test = data
|
||||
xmin = ymin = -4
|
||||
xmax = ymax = +4
|
||||
os.makedirs(model_name, exist_ok=True)
|
||||
```
|
||||
|
||||
```py
|
||||
filename = os.path.join(model_name, "latent_2dim.png")
|
||||
# display a 2D plot of the digit classes in the latent space
|
||||
z = encoder.predict(x_test,
|
||||
batch_size=batch_size)
|
||||
plt.figure(figsize=(12, 10))
|
||||
```
|
||||
|
||||
```py
|
||||
# axes x and y ranges
|
||||
axes = plt.gca()
|
||||
axes.set_xlim([xmin,xmax])
|
||||
axes.set_ylim([ymin,ymax])
|
||||
```
|
||||
|
||||
```py
|
||||
# subsample to reduce density of points on the plot
|
||||
z = z[0::2]
|
||||
y_test = y_test[0::2]
|
||||
plt.scatter(z[:, 0], z[:, 1], marker="")
|
||||
for i, digit in enumerate(y_test):
|
||||
axes.annotate(digit, (z[i, 0], z[i, 1]))
|
||||
plt.xlabel("z[0]")
|
||||
plt.ylabel("z[1]")
|
||||
plt.savefig(filename)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
```py
|
||||
filename = os.path.join(model_name, "digits_over_latent.png")
|
||||
# display a 30x30 2D manifold of the digits
|
||||
n = 30
|
||||
digit_size = 28
|
||||
figure = np.zeros((digit_size * n, digit_size * n))
|
||||
# linearly spaced coordinates corresponding to the 2D plot
|
||||
# of digit classes in the latent space
|
||||
grid_x = np.linspace(xmin, xmax, n)
|
||||
grid_y = np.linspace(ymin, ymax, n)[::-1]
|
||||
```
|
||||
|
||||
```py
|
||||
for i, yi in enumerate(grid_y):
|
||||
for j, xi in enumerate(grid_x):
|
||||
z = np.array([[xi, yi]])
|
||||
x_decoded = decoder.predict(z)
|
||||
digit = x_decoded[0].reshape(digit_size, digit_size)
|
||||
figure[i * digit_size: (i + 1) * digit_size,
|
||||
j * digit_size: (j + 1) * digit_size] = digit
|
||||
```
|
||||
|
||||
```py
|
||||
plt.figure(figsize=(10, 10))
|
||||
start_range = digit_size // 2
|
||||
end_range = n * digit_size + start_range + 1
|
||||
pixel_range = np.arange(start_range, end_range, digit_size)
|
||||
sample_range_x = np.round(grid_x, 1)
|
||||
sample_range_y = np.round(grid_y, 1)
|
||||
plt.xticks(pixel_range, sample_range_x)
|
||||
plt.yticks(pixel_range, sample_range_y)
|
||||
plt.xlabel("z[0]")
|
||||
plt.ylabel("z[1]")
|
||||
plt.imshow(figure, cmap='Greys_r')
|
||||
plt.savefig(filename)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
这样就完成了和自编码器的检查。 接下来的章节将重点介绍其实际应用。 我们将从去噪自编码器开始。
|
||||
|
||||
# 3\. 去噪自编码器(DAE)
|
||||
|
||||
现在,我们将构建具有实际应用的自编码器。 首先,让我们画一幅画,然后想象 MNIST 的数字图像被噪声破坏了,从而使人类更难以阅读。 我们能够构建一个去噪自编码器(DAE),以消除这些图像中的噪声。“图 3.3.1”向我们展示了三组 MNIST 数字。 每组的顶部行(例如,MNIST 数字 7、2、1、9、0、6、3、4 和 9)是原始图像。 中间的行显示了 DAE 的输入,这些输入是被噪声破坏的原始图像。 作为人类,我们发现很难读取损坏的 MNIST 数字。 最后一行显示 DAE 的输出。
|
||||
|
||||

|
||||
|
||||
图 3.3.1:原始 MNIST 数字(顶部行),损坏的原始图像(中间行)和去噪图像(最后一行)
|
||||
|
||||
如图“图 3.3.2”所示,去噪自编码器的结构实际上与我们在上一节中介绍的 MNIST 的自编码器相同。
|
||||
|
||||

|
||||
|
||||
图 3.3.2:去噪自编码器的输入是损坏的图像。 输出是干净或去噪的图像。 假定潜向量为 16 维
|
||||
|
||||
“图 3.3.2”中的输入定义为:
|
||||
|
||||
`x = x_ori + noise`(公式 3.3.1)
|
||||
|
||||
在该公式中,`x_ori`表示被*噪声*破坏的原始 MNIST 图像。 编码器的目的是发现如何产生潜向量`z`,这将使解码器能够恢复诸如 MSE,如下所示:`x_ori`通过最小化相异损失函数:
|
||||
|
||||
 (Equation 3.3.2)
|
||||
|
||||
在此示例中,`m`是输出尺寸(例如,在 MNIST 中,`m = width × height × channels = 28 × 28 × 1 = 784`)。 `x_ori[i]`和`x_tilde[i]`分别是`x_ori`和`x_tilde`的元素。
|
||||
|
||||
为了实现 DAE,我们将需要对上一节中介绍的自编码器进行一些更改。 首先,训练输入数据应损坏的 MNIST 数字。 训练输出数据是原始的原始 MNIST 数字相同。 这就像告诉自编码器应校正的图像是什么,或要求它找出在图像损坏的情况下如何消除噪声。 最后,我们必须在损坏的 MNIST 测试数据上验证自编码器。
|
||||
|
||||
“图 3.3.2"左侧所示的 MNIST 数字 7 是实际损坏的图像输入。 右边的是经过训练的降噪自编码器的干净图像输出。
|
||||
|
||||
“列表 3.3.1”:`denoising-autoencoder-mnist-3.3.1.py`
|
||||
|
||||
```py
|
||||
from tensorflow.keras.layers import Dense, Input
|
||||
from tensorflow.keras.layers import Conv2D, Flatten
|
||||
from tensorflow.keras.layers import Reshape, Conv2DTranspose
|
||||
from tensorflow.keras.models import Model
|
||||
from tensorflow.keras import backend as K
|
||||
from tensorflow.keras.datasets import mnist
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from PIL import Image
|
||||
```
|
||||
|
||||
```py
|
||||
np.random.seed(1337)
|
||||
```
|
||||
|
||||
```py
|
||||
# load MNIST dataset
|
||||
(x_train, _), (x_test, _) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape to (28, 28, 1) and normalize input images
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
|
||||
x_test = np.reshape(x_test, [-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# generate corrupted MNIST images by adding noise with normal dist
|
||||
# centered at 0.5 and std=0.5
|
||||
noise = np.random.normal(loc=0.5, scale=0.5, size=x_train.shape)
|
||||
x_train_noisy = x_train + noise
|
||||
```
|
||||
|
||||
```py
|
||||
noise = np.random.normal(loc=0.5, scale=0.5, size=x_test.shape)
|
||||
x_test_noisy = x_test + noise
|
||||
# adding noise may exceed normalized pixel values>1.0 or <0.0
|
||||
# clip pixel values >1.0 to 1.0 and <0.0 to 0.0
|
||||
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
|
||||
x_test_noisy = np.clip(x_test_noisy, 0., 1.)
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 32
|
||||
kernel_size = 3
|
||||
latent_dim = 16
|
||||
# encoder/decoder number of CNN layers and filters per layer
|
||||
layer_filters = [32, 64]
|
||||
```
|
||||
|
||||
```py
|
||||
# build the autoencoder model
|
||||
# first build the encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
x = inputs
|
||||
```
|
||||
|
||||
```py
|
||||
# stack of Conv2D(32)-Conv2D(64)
|
||||
for filters in layer_filters:
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# shape info needed to build decoder model so we don't do hand computation
|
||||
# the input to the decoder's first Conv2DTranspose will have this shape
|
||||
# shape is (7, 7, 64) which can be processed by the decoder back to (28, 28, 1)
|
||||
shape = K.int_shape(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate the latent vector
|
||||
x = Flatten()(x)
|
||||
latent = Dense(latent_dim, name='latent_vector')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model(inputs, latent, name='encoder')
|
||||
encoder.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# build the decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='decoder_input')
|
||||
# use the shape (7, 7, 64) that was earlier saved
|
||||
x = Dense(shape[1] * shape[2] * shape[3])(latent_inputs)
|
||||
# from vector to suitable shape for transposed conv
|
||||
x = Reshape((shape[1], shape[2], shape[3]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# stack of Conv2DTranspose(64)-Conv2DTranspose(32)
|
||||
for filters in layer_filters[::-1]:
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# reconstruct the denoised input
|
||||
outputs = Conv2DTranspose(filters=1,
|
||||
kernel_size=kernel_size,
|
||||
padding='same',
|
||||
activation='sigmoid',
|
||||
name='decoder_output')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model(latent_inputs, outputs, name='decoder')
|
||||
decoder.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# autoencoder = encoder + decoder
|
||||
# instantiate autoencoder model
|
||||
autoencoder = Model(inputs, decoder(encoder(inputs)), name='autoencoder')
|
||||
autoencoder.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# Mean Square Error (MSE) loss function, Adam optimizer
|
||||
autoencoder.compile(loss='mse', optimizer='adam')
|
||||
```
|
||||
|
||||
```py
|
||||
# train the autoencoder
|
||||
autoencoder.fit(x_train_noisy,
|
||||
x_train,
|
||||
validation_data=(x_test_noisy, x_test),
|
||||
epochs=10,
|
||||
batch_size=batch_size)
|
||||
```
|
||||
|
||||
```py
|
||||
# predict the autoencoder output from corrupted test images
|
||||
x_decoded = autoencoder.predict(x_test_noisy)
|
||||
```
|
||||
|
||||
```py
|
||||
# 3 sets of images with 9 MNIST digits
|
||||
# 1st rows - original images
|
||||
# 2nd rows - images corrupted by noise
|
||||
# 3rd rows - denoised images
|
||||
rows, cols = 3, 9
|
||||
num = rows * cols
|
||||
imgs = np.concatenate([x_test[:num], x_test_noisy[:num], x_decoded[:num]])
|
||||
imgs = imgs.reshape((rows * 3, cols, image_size, image_size))
|
||||
imgs = np.vstack(np.split(imgs, rows, axis=1))
|
||||
imgs = imgs.reshape((rows * 3, -1, image_size, image_size))
|
||||
imgs = np.vstack([np.hstack(i) for i in imgs])
|
||||
imgs = (imgs * 255).astype(np.uint8)
|
||||
plt.figure()
|
||||
plt.axis('off')
|
||||
plt.title('Original images: top rows, '
|
||||
'Corrupted Input: middle rows, '
|
||||
'Denoised Input: third rows')
|
||||
plt.imshow(imgs, interpolation='none', cmap='gray')
|
||||
Image.fromarray(imgs).save('corrupted_and_denoised.png')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
“列表 3.3.1”显示了去噪自编码器,该编码器已添加到官方 Keras GitHub 存储库中。 使用相同的 MNIST 数据集,我们可以通过添加随机噪声来模拟损坏的图像。 添加的噪声是高斯分布,平均值为`μ = 0.5`,标准差为`σ = 0.5`。 由于添加随机噪声可能会将像素数据推入小于 0 或大于 1 的无效值,因此像素值会被裁剪为`[0.1, 1.0]`范围。
|
||||
|
||||
其他所有内容实际上都与上一节中的自编码器相同。 我们将使用相同的 MSE 损失函数和 Adam 优化器。 但是,训练的周期数已增加到 10。这是为了进行足够的参数优化。
|
||||
|
||||
“图 3.3.3”显示了 DAE 在某种程度上的鲁棒性,因为噪声级别从`σ = 0.5`增至`σ = 0.75`和`σ = 1.0`。 在`σ = 0.75`处,DAE 仍能够恢复原始图像。 但是,在`σ = 1.0`处,一些数字,例如第二和第三组中的 4 和 5,将无法正确恢复。
|
||||
|
||||

|
||||
|
||||
图 3.3.3:降噪自编码器的表现随着噪声水平的提高而增加
|
||||
|
||||
我们已经完成去噪自编码器的讨论和实现。 尽管此概念已在 MNIST 数字上进行了演示,但该思想也适用于其他信号。 在下一节中,我们将介绍自编码器的另一种实际应用,称为着色自编码器。
|
||||
|
||||
# 4\. 自动着色自编码器
|
||||
|
||||
现在,我们将致力于自编码器的另一个实际应用。 在这种情况下,我们将想象一下,我们有一张灰度照片,并且想要构建一个可以自动为其添加颜色的工具。 我们要复制人类的能力,以识别海洋和天空为蓝色,草地和树木为绿色,云层为白色,依此类推。
|
||||
|
||||
如图“图 3.4.1”所示,如果给我们前景的稻田,背景的火山和顶部的天空的灰度照片(左),我们可以添加适当的颜色(右)。
|
||||
|
||||

|
||||
|
||||
图 3.4.1:为 Mayon 火山的灰度照片添加颜色。 着色网络应通过向灰度照片添加颜色来复制人类的能力。 左照片是灰度的。 正确的照片是彩色的。 原始彩色照片可以在本书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter3-autoencoders/README.md)中找到。
|
||||
|
||||
对于自编码器,一种简单的自动着色算法似乎是一个合适的问题。 如果我们可以使用足够数量的灰度照片作为输入并使用相应的彩色照片作为输出来训练自编码器,则可能会在正确应用颜色时发现隐藏的结构。 大致上,这是去噪的反向过程。 问题是,自编码器能否在原始灰度图像上添加颜色(良好的噪点)?
|
||||
|
||||
“列表 3.4.1”显示了着色自编码器网络。 着色自编码器网络是我们用于 MNIST 数据集的降噪自编码器的修改版本。 首先,我们需要一个彩色照片的灰度数据集。 我们之前使用过的 CIFAR10 数据库进行了 50,000 次训练和 10,000 次测试,可以将`32×32` RGB 照片转换为灰度图像。 如下清单所示,我们可以使用`rgb2gray()`函数在 R,G 和 B 分量上应用权重,以从彩色转换为灰度:
|
||||
|
||||
“列表 3.4.1”:`colorization-autoencoder-cifar10-3.4.1.py`
|
||||
|
||||
```py
|
||||
from tensorflow.keras.layers import Dense, Input
|
||||
from tensorflow.keras.layers import Conv2D, Flatten
|
||||
from tensorflow.keras.layers import Reshape, Conv2DTranspose
|
||||
from tensorflow.keras.models import Model
|
||||
from tensorflow.keras.callbacks import ReduceLROnPlateau
|
||||
from tensorflow.keras.callbacks import ModelCheckpoint
|
||||
from tensorflow.keras.datasets import cifar10
|
||||
from tensorflow.keras.utils import plot_model
|
||||
from tensorflow.keras import backend as K
|
||||
```
|
||||
|
||||
```py
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
```
|
||||
|
||||
```py
|
||||
def rgb2gray(rgb):
|
||||
"""Convert from color image (RGB) to grayscale.
|
||||
Source: opencv.org
|
||||
grayscale = 0.299*red + 0.587*green + 0.114*blue
|
||||
Argument:
|
||||
rgb (tensor): rgb image
|
||||
Return:
|
||||
(tensor): grayscale image
|
||||
"""
|
||||
return np.dot(rgb[...,:3], [0.299, 0.587, 0.114])
|
||||
```
|
||||
|
||||
```py
|
||||
# load the CIFAR10 data
|
||||
(x_train, _), (x_test, _) = cifar10.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# input image dimensions
|
||||
# we assume data format "channels_last"
|
||||
img_rows = x_train.shape[1]
|
||||
img_cols = x_train.shape[2]
|
||||
channels = x_train.shape[3]
|
||||
# create saved_images folder
|
||||
imgs_dir = 'saved_images'
|
||||
save_dir = os.path.join(os.getcwd(), imgs_dir)
|
||||
if not os.path.isdir(save_dir):
|
||||
os.makedirs(save_dir)
|
||||
```
|
||||
|
||||
```py
|
||||
# display the 1st 100 input images (color and gray)
|
||||
imgs = x_test[:100]
|
||||
imgs = imgs.reshape((10, 10, img_rows, img_cols, channels))
|
||||
imgs = np.vstack([np.hstack(i) for i in imgs])
|
||||
plt.figure()
|
||||
plt.axis('off')
|
||||
plt.title('Test color images (Ground Truth)')
|
||||
plt.imshow(imgs, interpolation='none')
|
||||
plt.savefig('%s/test_color.png' % imgs_dir)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
```py
|
||||
# convert color train and test images to gray
|
||||
x_train_gray = rgb2gray(x_train)
|
||||
x_test_gray = rgb2gray(x_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# display grayscale version of test images
|
||||
imgs = x_test_gray[:100]
|
||||
imgs = imgs.reshape((10, 10, img_rows, img_cols))
|
||||
imgs = np.vstack([np.hstack(i) for i in imgs])
|
||||
plt.figure()
|
||||
plt.axis('off')
|
||||
plt.title('Test gray images (Input)')
|
||||
plt.imshow(imgs, interpolation='none', cmap='gray')
|
||||
plt.savefig('%s/test_gray.png' % imgs_dir)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
```py
|
||||
# normalize output train and test color images
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# normalize input train and test grayscale images
|
||||
x_train_gray = x_train_gray.astype('float32') / 255
|
||||
x_test_gray = x_test_gray.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape images to row x col x channel for CNN output/validation
|
||||
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, channels)
|
||||
x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, channels)
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape images to row x col x channel for CNN input
|
||||
x_train_gray = x_train_gray.reshape(x_train_gray.shape[0], img_rows, img_cols, 1)
|
||||
x_test_gray = x_test_gray.reshape(x_test_gray.shape[0], img_rows, img_cols, 1)
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (img_rows, img_cols, 1)
|
||||
batch_size = 32
|
||||
kernel_size = 3
|
||||
latent_dim = 256
|
||||
# encoder/decoder number of CNN layers and filters per layer
|
||||
layer_filters = [64, 128, 256]
|
||||
```
|
||||
|
||||
```py
|
||||
# build the autoencoder model
|
||||
# first build the encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
x = inputs
|
||||
# stack of Conv2D(64)-Conv2D(128)-Conv2D(256)
|
||||
for filters in layer_filters:
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# shape info needed to build decoder model so we don't do hand computation
|
||||
# the input to the decoder's first Conv2DTranspose will have this shape
|
||||
# shape is (4, 4, 256) which is processed by the decoder back to (32, 32, 3)
|
||||
shape = K.int_shape(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate a latent vector
|
||||
x = Flatten()(x)
|
||||
latent = Dense(latent_dim, name='latent_vector')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model(inputs, latent, name='encoder')
|
||||
encoder.summary()
|
||||
# build the decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='decoder_input')
|
||||
x = Dense(shape[1]*shape[2]*shape[3])(latent_inputs)
|
||||
x = Reshape((shape[1], shape[2], shape[3]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# stack of Conv2DTranspose(256)-Conv2DTranspose(128)-Conv2DTranspose(64)
|
||||
for filters in layer_filters[::-1]:
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
outputs = Conv2DTranspose(filters=channels,
|
||||
kernel_size=kernel_size,
|
||||
activation='sigmoid',
|
||||
padding='same',
|
||||
name='decoder_output')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model(latent_inputs, outputs, name='decoder')
|
||||
decoder.summary()
|
||||
# autoencoder = encoder + decoder
|
||||
# instantiate autoencoder model
|
||||
autoencoder = Model(inputs, decoder(encoder(inputs)), name='autoencoder')
|
||||
autoencoder.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# prepare model saving directory.
|
||||
save_dir = os.path.join(os.getcwd(), 'saved_models')
|
||||
model_name = 'colorized_ae_model.{epoch:03d}.h5'
|
||||
if not os.path.isdir(save_dir):
|
||||
os.makedirs(save_dir)
|
||||
filepath = os.path.join(save_dir, model_name)
|
||||
```
|
||||
|
||||
```py
|
||||
# reduce learning rate by sqrt(0.1) if the loss does not improve in 5 epochs
|
||||
lr_reducer = ReduceLROnPlateau(factor=np.sqrt(0.1),
|
||||
cooldown=0,
|
||||
patience=5,
|
||||
verbose=1,
|
||||
min_lr=0.5e-6)
|
||||
# save weights for future use (e.g. reload parameters w/o training)
|
||||
checkpoint = ModelCheckpoint(filepath=filepath,
|
||||
monitor='val_loss',
|
||||
verbose=1,
|
||||
save_best_only=True)
|
||||
```
|
||||
|
||||
```py
|
||||
# Mean Square Error (MSE) loss function, Adam optimizer
|
||||
autoencoder.compile(loss='mse', optimizer='adam')
|
||||
```
|
||||
|
||||
```py
|
||||
# called every epoch
|
||||
callbacks = [lr_reducer, checkpoint]
|
||||
```
|
||||
|
||||
```py
|
||||
# train the autoencoder
|
||||
autoencoder.fit(x_train_gray,
|
||||
x_train,
|
||||
validation_data=(x_test_gray, x_test),
|
||||
epochs=30,
|
||||
batch_size=batch_size,
|
||||
callbacks=callbacks)
|
||||
# predict the autoencoder output from test data
|
||||
x_decoded = autoencoder.predict(x_test_gray)
|
||||
```
|
||||
|
||||
```py
|
||||
# display the 1st 100 colorized images
|
||||
imgs = x_decoded[:100]
|
||||
imgs = imgs.reshape((10, 10, img_rows, img_cols, channels))
|
||||
imgs = np.vstack([np.hstack(i) for i in imgs])
|
||||
plt.figure()
|
||||
plt.axis('off')
|
||||
plt.title('Colorized test images (Predicted)')
|
||||
plt.imshow(imgs, interpolation='none')
|
||||
plt.savefig('%s/colorized.png' % imgs_dir)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
通过添加更多卷积和转置卷积,我们提高了自编码器的容量。 我们还将每个 CNN 块的过滤器数量增加了一倍。 潜向量现在为 256 维,以增加其可以表示的显着属性的数量,如自编码器部分所述。 最后,输出过滤器的大小已增加到三倍,或等于预期的彩色输出的 RGB 中的通道数。
|
||||
|
||||
现在使用灰度作为输入,原始 RGB 图像作为输出来训练着色自编码器。 训练将花费更多的时间,并在验证损失没有改善的情况下使用学习率降低器来缩小学习率。 通过告诉`tf.keras fit()`函数中的 callbacks 参数调用`lr_reducer()`函数,可以轻松完成此操作。
|
||||
|
||||
“图 3.4.2”演示了来自 CIFAR10 测试数据集的灰度图像的着色。
|
||||
|
||||

|
||||
|
||||
图 3.4.2:使用自编码器将灰度自动转换为彩色图像。 CIFAR10 测试灰度输入图像(左)和预测的彩色图像(右)。 原始彩色照片可以在本书的 GitHub 存储库中找到,网址为 https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter3-autoencoders/README.md
|
||||
|
||||
“图 3.4.3”将基本事实与着色自编码器预测进行了比较:
|
||||
|
||||

|
||||
|
||||
图 3.4.3:地面真彩色图像与预测彩色图像的并排比较。 原始彩色照片可以在本书的 GitHub 存储库中找到,网址为 https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter3-autoencoders/README.md
|
||||
|
||||
自编码器执行可接受的着色作业。 预计大海或天空为蓝色,动物的阴影为棕色,云为白色,依此类推。
|
||||
|
||||
有一些明显的错误预测,例如红色车辆变成蓝色或蓝色车辆变成红色,偶尔的绿色领域被误认为是蓝天,而黑暗或金色的天空被转换为蓝天。
|
||||
|
||||
这是关于自编码器的最后一部分。 在以下各章中,我们将重新讨论以一种或另一种形式进行编码和解码的概念。 表示学习的概念在深度学习中非常基础。
|
||||
|
||||
# 5\. 总结
|
||||
|
||||
在本章中,我们已经介绍了自编码器,它们是将输入数据压缩为低维表示形式的神经网络,以便有效地执行结构转换,例如降噪和着色。 我们为 GAN 和 VAE 的更高级主题奠定了基础,我们将在后面的章节中介绍它们。 我们已经演示了如何从两个构建模块模型(编码器和解码器)实现自编码器。 我们还学习了如何提取输入分布的隐藏结构是 AI 的常见任务之一。
|
||||
|
||||
一旦学习了潜在代码,就可以对原始输入分布执行许多结构操作。 为了更好地了解输入分布,可以使用低级嵌入(类似于本章内容)或通过更复杂的降维技术(例如 t-SNE 或 PCA)来可视化潜在向量形式的隐藏结构。
|
||||
|
||||
除了去噪和着色外,自编码器还用于将输入分布转换为低维潜向量,可以针对其他任务(例如,分割,检测,跟踪,重建和视觉理解)进一步对其进行处理。 在“第 8 章”,“变分自编码器(VAE)”中,我们将讨论 VAE,它们在结构上与自编码器相同,但具有可解释的潜在代码,这些代码可以产生连续的潜在向量投影,因此有所不同。
|
||||
|
||||
在下一章中,我们将着手介绍 AI 最近最重要的突破之一,即 GAN。 在下一章中,我们将学习 GAN 的核心优势,即其综合看起来真实的数据的能力。
|
||||
|
||||
# 6\. 参考
|
||||
|
||||
1. `Ian Goodfellow et al.: Deep Learning. Vol. 1. Cambridge: MIT press, 2016 (http://www.deeplearningbook.org/).`
|
||||
@@ -1,784 +0,0 @@
|
||||
# 四、生成对抗网络(GAN)
|
||||
|
||||
在本章中,我们将研究**生成对抗网络**(**GAN**)[1]。 GAN 属于生成模型家族。 但是,与自编码器不同,生成模型能够在给定任意编码的情况下创建新的有意义的输出。
|
||||
|
||||
在本章中,将讨论 GAN 的工作原理。 我们还将使用`tf.keras`回顾几个早期 GAN 的实现,而在本章的后面,我们将演示实现稳定训练所需的技术。 本章的范围涵盖了 GAN 实现的两个流行示例,**深度卷积 GAN**(**DCGAN**)[2]和**条件 GAN**(**CGAN**)[3]。
|
||||
|
||||
总之,本章的目标是:
|
||||
|
||||
* GAN 的原理简介
|
||||
* GAN 的早期工作实现之一的简介,称为 DCGAN
|
||||
* 改进的 DCGAN,称为 CGAN,它使用条件
|
||||
* 在`tf.keras`中实现 DCGAN 和 CGAN
|
||||
|
||||
让我们从 GAN 的概述开始。
|
||||
|
||||
# 1\. GAN 概述
|
||||
|
||||
在进入 GAN 的更高级概念之前,让我们开始研究 GAN,并介绍它们背后的基本概念。 GAN 非常强大。 通过执行潜在空间插值,他们可以生成不是真实人的新人脸这一事实证明了这一简单的陈述。
|
||||
|
||||
可以在以下 YouTube 视频中看到 GAN 的高级功能:
|
||||
|
||||
* [Progressive GAN [4]](https://youtu.be/G06dEcZ-QTg)
|
||||
* [StyleGAN v1 [5]](https://youtu.be/kSLJriaOumA)
|
||||
* [StyleGAN v2 [6]](https://youtu.be/c-NJtV9Jvp0)
|
||||
|
||||
展示如何利用 GAN 产生逼真的面部的视频演示了它们的功能。 这个主题比我们之前看过的任何内容都先进得多。 例如,上面的视频演示了自编码器无法轻松完成的事情,我们在“第 3 章”,“自编码器”中介绍了这些内容。
|
||||
|
||||
GAN 可以通过训练两个相互竞争(且相互配合)的网络(称为**生成器**和**判别器**(有时称为**评论家**)。 生成器的作用是继续弄清楚如何生成伪造数据或信号(包括音频和图像),使伪造者蒙上阴影。 同时,判别器被训练以区分假信号和真实信号。 随着训练的进行,判别器将不再能够看到合成生成的数据与真实数据之间的差异。 从那里,可以丢弃判别器,然后可以使用生成器来创建从未见过的新的真实数据。
|
||||
|
||||
GAN 的基本概念很简单。 但是,我们将发现的一件事是,最具挑战性的问题是我们如何实现对生成器-判别器网络的稳定训练? 为了使两个网络都能同时学习,生成器和判别器之间必须存在健康的竞争。 由于损失函数是根据判别器的输出计算得出的,因此其参数会快速更新。 当判别器收敛速度更快时,生成器不再为其参数接收到足够的梯度更新,并且无法收敛。 除了难以训练之外,GAN 还可能遭受部分或全部模态崩溃的影响,这种情况下,生成器针对不同的潜在编码生成几乎相似的输出。
|
||||
|
||||
## GAN 的原理
|
||||
|
||||
如图“图 4.1.1”所示,GAN 类似于伪造者(生成器)-警察(判别器)场景。 在学院里,警察被教导如何确定美钞是真钞还是假钞。 来自银行的真实美钞样本和来自伪造者的伪钞样本被用来训练警察。 但是,伪造者会不时地假装他印制了真实的美元钞票。 最初,警方不会上当,并且会告诉造假者这笔钱是假的。 考虑到此反馈,造假者再次磨练他的技能,并尝试制作新的假美元钞票。 如预期的那样,警察将能够发现这笔钱是伪造的,并说明为什么美元钞票是伪造的:
|
||||
|
||||

|
||||
|
||||
图 4.1.1:GAN 的生成器和判别器类似于伪造者和警察。 造假者的目的是欺骗警察,使他们相信美元钞票是真实的
|
||||
|
||||
此过程无限期地继续,但是到了造假者已经掌握了伪造货币的程度,以至于伪造品与真实货币几乎无法区分-甚至对于最受执业的警察也是如此。 然后,伪造者可以无限次打印美元钞票,而不会被警方抓获,因为它们不再可识别为伪造的。
|
||||
|
||||
如图“图 4.1.2”所示,GAN 由两个网络组成,一个生成器和一个判别器:
|
||||
|
||||

|
||||
|
||||
图 4.1.2:GAN 由两个网络组成,一个生成器和一个判别器。 判别器经过训练,可以区分真实信号和虚假信号或数据。 生成器的工作是生成伪造的信号或数据,这些伪造的信号或数据最终会欺骗判别器
|
||||
|
||||
生成器的输入是噪声,输出是合成数据。 同时,判别器的输入将是实数据或合成数据。 真实数据来自真实的采样数据,而虚假数据来自生成器。 所有有效数据均标记为 1.0(即 100% 为真实概率),而所有合成数据均标记为 0.0(即 0% 为真实概率)。 由于标记过程是自动化的,因此 GAN 仍被认为是深度学习中无监督学习方法的一部分。
|
||||
|
||||
判别器的目标是从此提供的数据集中学习如何区分真实数据与伪数据。 在 GAN 训练的这一部分中,仅判别器参数将被更新。 像典型的二元分类器一样,判别器经过训练,可以在 0.0 到 1.0 的范围内预测置信度值,以了解给定输入数据与真实数据的接近程度。 但是,这只是故事的一半。
|
||||
|
||||
生成器将以固定的时间间隔假装其输出是真实数据,并要求 GAN 将其标记为 1.0。 然后,当将伪造数据提供给判别器时,自然会将其分类为伪造,标签接近 0.0。
|
||||
|
||||
优化器根据显示的标签(即 1.0)计算生成器参数更新。 在对新数据进行训练时,它还会考虑自己的预测。 换句话说,判别器对其预测有一些疑问,因此,GAN 将其考虑在内。 这次,GAN 将让梯度从判别器的最后一层向下向下传播到生成器的第一层。 但是,在大多数实践中,在训练的此阶段,判别器参数会暂时冻结。 生成器将使用梯度来更新其参数并提高其合成伪数据的能力。
|
||||
|
||||
总体而言,整个过程类似于两个网络相互竞争,同时仍在合作。 当 GAN 训练收敛时,最终结果是生成器,可以合成看似真实的数据。 判别器认为该合成数据是真实数据或带有接近 1.0 的标签,这意味着该判别器可以被丢弃。 生成器部分将有助于从任意噪声输入中产生有意义的输出。
|
||||
|
||||
下面的“图 4.1.3”中概述了该过程:
|
||||
|
||||

|
||||
|
||||
图 4.1.3:训练判别器类似于使用二进制交叉熵损失训练二分类器网络。 伪数据由生成器提供,而真实数据来自真实样本
|
||||
|
||||
如上图所示,可以通过最小化以下等式中的损失函数来训练判别器:
|
||||
|
||||
 (Equation 4.1.1)
|
||||
|
||||
该方程只是标准的二进制交叉熵代价函数。 损失是正确识别真实数据`1 - D(g(z))`的期望值与 1.0 正确识别合成数据`1 - D(g(z))`的期望值之和。 日志不会更改本地最小值的位置。
|
||||
|
||||
训练过程中将两个小批数据提供给判别器:
|
||||
|
||||
1. `x`,来自采样数据的实数据(换言之,`x ~ p_data`),标签为 1.0
|
||||
|
||||
1. `x' = g(z)`,来自生成器的带有标签 0.0 的伪造数据
|
||||
|
||||
为了使的损失函数最小,将通过反向传播通过正确识别真实数据`D(x)`和合成数据`1 - D(g(z))`来更新判别器参数`θ^(D)`。 正确识别真实数据等同于`D(x) -> 1.0`,而正确分类伪造数据则与`D(g(z)) -> 0.0`或`1 - D(g(z)) -> 1.0`相同。 在此等式中,`z`是生成器用来合成新信号的任意编码或噪声向量。 两者都有助于最小化损失函数。
|
||||
|
||||
为了训练生成器,GAN 将判别器和生成器损失的总和视为零和博弈。 生成器损失函数只是判别器损失函数的负数:
|
||||
|
||||
 (Equation 4.1.2)
|
||||
|
||||
然后可以将其更恰当地重写为值函数:
|
||||
|
||||
 (Equation 4.1.3)
|
||||
|
||||
从生成器的角度来看,应将“公式 4.1.3”最小化。 从判别器的角度来看,值函数应最大化。 因此,生成器训练准则可以写成极大极小问题:
|
||||
|
||||
 (Equation 4.1.4)
|
||||
|
||||
有时,我们会假装合成数据是带有标签 1.0 的真实数据,以此来欺骗判别器。 通过最大化`θ^(D)`,优化器将梯度更新发送到判别器参数,以将该合成数据视为真实数据。 同时,通过将`θ^(G)`的相关性减至最小,优化器将在上训练生成器的参数,从而欺骗识别器。 但是,实际上,判别器对将合成数据分类为伪造的预测很有信心,并且不会更新 GAN 参数。 此外,梯度更新很小,并且在传播到生成器层时已大大减小。 结果,生成器无法收敛。
|
||||
|
||||

|
||||
|
||||
图 4.1.4:训练生成器就像使用二进制交叉熵损失函数训练网络一样。 来自生成器的虚假数据显示为真实数据
|
||||
|
||||
解决方案是按以下形式重新构造生成器的损失函数:
|
||||
|
||||
 (Equation 4.1.5)
|
||||
|
||||
损失函数只是通过训练生成器,最大程度地提高了判别器认为合成数据是真实数据的机会。 新公式不再是零和,而是纯粹由启发式驱动的。“图 4.1.4”显示了训练过程中的生成器。 在此图中,仅在训练整个对抗网络时才更新生成器参数。 这是因为梯度从判别器向下传递到生成器。 但是,实际上,判别器权重仅在对抗训练期间临时冻结。
|
||||
|
||||
在深度学习中,可以使用合适的神经网络架构来实现生成器和判别器。 如果数据或信号是图像,则生成器和判别器网络都将使用 CNN。 对于诸如音频之类的一维序列,两个网络通常都是循环的(RNN,LSTM 或 GRU)。
|
||||
|
||||
在本节中,我们了解到 GAN 的原理很简单。 我们还了解了如何通过熟悉的网络层实现 GAN。 GAN 与其他网络的区别在于众所周知,它们很难训练。 只需稍作更改,就可以使网络变得不稳定。 在以下部分中,我们将研究使用深度 CNN 的 GAN 早期成功实现之一。 它称为 DCGAN [3]。
|
||||
|
||||
# 2\. 在 Keras 中实现 DCGAN
|
||||
|
||||
“图 4.2.1”显示 DCGAN,其中用于生成伪造的 MNIST 图像:
|
||||
|
||||

|
||||
|
||||
图 4.2.1:DCGAN 模型
|
||||
|
||||
DCGAN 实现以下设计原则:
|
||||
|
||||
* 使用`stride > 1`和卷积代替`MaxPooling2D`或`UpSampling2D`。 通过`stride > 1`,CNN 可以学习如何调整特征映射的大小。
|
||||
* 避免使用`Dense`层。 在所有层中使用 CNN。 `Dense`层仅用作生成器的第一层以接受`z`向量。 调整`Dense`层的输出大小,并成为后续 CNN 层的输入。
|
||||
* 使用**批量归一化**(**BN**),通过将每一层的输入归一化以使均值和单位方差为零,来稳定学习。 生成器输出层和判别器输入层中没有 BN。 在此处要介绍的实现示例中,没有在标识符中使用批量归一化。
|
||||
* **整流线性单元**(**ReLU**)在生成器的所有层中均使用,但在输出层中则使用`tanh`激活。 在此处要介绍的实现示例中,在生成器的输出中使用`sigmoid`代替`tanh`,因为通常会导致对 MNIST 数字进行更稳定的训练。
|
||||
* 在判别器的所有层中使用 **Leaky ReLU**。 与 ReLU 不同,Leaky ReLU 不会在输入小于零时将所有输出清零,而是生成一个等于`alpha x input`的小梯度。 在以下示例中,`alpha = 0.2`。
|
||||
|
||||
生成器学习从 100 维输入向量(`[-1.0,1.0]`范围内具有均匀分布的 100 维随机噪声)生成伪图像。 判别器将真实图像与伪图像分类,但是在训练对抗网络时无意中指导生成器如何生成真实图像。 在我们的 DCGAN 实现中使用的核大小为 5。这是为了允许它增加卷积的接收场大小和表达能力。
|
||||
|
||||
生成器接受由 -1.0 到 1.0 范围内的均匀分布生成的 100 维`z`向量。 生成器的第一层是`7 x 7 x 128 = 6,272`单元的密集层。 基于输出图像的预期最终尺寸(`28 x 28 x 1`,28 是 7 的倍数)和第一个`Conv2DTranspose`的过滤器数量(等于 128)来计算单元数量。
|
||||
|
||||
我们可以将转置的 CNN(`Conv2DTranspose`)想象成 CNN 的逆过程。 在一个简单的示例中,如果 CNN 将图像转换为特征映射,则转置的 CNN 将生成给定特征映射的图像。 因此,转置的 CNN 在上一章的解码器中和本章的生成器中使用。
|
||||
|
||||
在对`strides = 2`进行两个`Conv2DTranspose`之后,特征映射的大小将为`28 x 28 x n_filter`。 每个`Conv2DTranspose`之前都有批量规范化和 ReLU。 最后一层具有 *Sigmoid* 激活,可生成`28 x 28 x 1`假 MNIST 图像。 将每个像素标准化为与`[0, 255]`灰度级相对应的`[0.0, 1.0]`。 下面的“列表 4.2.1”显示了`tf.keras`中生成器网络的实现。 定义了一个函数来生成生成器模型。 由于整个代码的长度,我们将列表限制为正在讨论的特定行。
|
||||
|
||||
[完整的代码可在 GitHub 上获得](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
“列表 4.2.1”:`dcgan-mnist-4.2.1.py`
|
||||
|
||||
```py
|
||||
def build_generator(inputs, image_size):
|
||||
"""Build a Generator Model
|
||||
```
|
||||
|
||||
```py
|
||||
Stack of BN-ReLU-Conv2DTranpose to generate fake images
|
||||
Output activation is sigmoid instead of tanh in [1].
|
||||
Sigmoid converges easily.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
inputs (Layer): Input layer of the generator
|
||||
the z-vector)
|
||||
image_size (tensor): Target size of one side
|
||||
(assuming square image)
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
generator (Model): Generator Model
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
image_resize = image_size // 4
|
||||
# network parameters
|
||||
kernel_size = 5
|
||||
layer_filters = [128, 64, 32, 1]
|
||||
```
|
||||
|
||||
```py
|
||||
x = Dense(image_resize * image_resize * layer_filters[0])(inputs)
|
||||
x = Reshape((image_resize, image_resize, layer_filters[0]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
for filters in layer_filters:
|
||||
# first two convolution layers use strides = 2
|
||||
# the last two use strides = 1
|
||||
if filters > layer_filters[-2]:
|
||||
strides = 2
|
||||
else:
|
||||
strides = 1
|
||||
x = BatchNormalization()(x)
|
||||
x = Activation('relu')(x)
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
x = Activation('sigmoid')(x)
|
||||
generator = Model(inputs, x, name='generator')
|
||||
return generator
|
||||
```
|
||||
|
||||
判别器与相似,是许多基于 CNN 的分类器。 输入是`28 x 28 x 1`MNIST 图像,分类为真实(1.0)或伪(0.0)。 有四个 CNN 层。 除了最后的卷积,每个`Conv2D`都使用`strides = 2`将特征映射下采样两个。 然后每个`Conv2D`之前都有一个泄漏的 ReLU 层。 最终的过滤器大小为 256,而初始的过滤器大小为 32,并使每个卷积层加倍。 最终的过滤器大小 128 也适用。 但是,我们会发现生成的图像在 256 的情况下看起来更好。最终输出层被展平,并且在通过 Sigmoid 激活层缩放后,单个单元`Dense`层在 0.0 到 1.0 之间生成预测。 输出被建模为伯努利分布。 因此,使用了二进制交叉熵损失函数。
|
||||
|
||||
建立生成器和判别器模型后,通过将生成器和判别器网络连接起来,建立对抗模型。 鉴别网络和对抗网络都使用 RMSprop 优化器。 判别器的学习率是`2e-4`,而对抗网络的学习率是`1e-4`。 判别器的 RMSprop 衰减率为`6e-8`,对抗网络的 RMSprop 衰减率为`3e-8`。
|
||||
|
||||
将对手的学习率设置为判别器的一半将使训练更加稳定。 您会从“图 4.1.3”和“图 4.1.4”中回忆起,GAN 训练包含两个部分:判别器训练和生成器训练,这是冻结判别器权重的对抗训练。
|
||||
|
||||
“列表 4.2.2”显示了`tf.keras`中判别器的实现。 定义一个函数来建立鉴别模型。
|
||||
|
||||
“列表 4.2.2”:`dcgan-mnist-4.2.1.py`
|
||||
|
||||
```py
|
||||
def build_discriminator(inputs):
|
||||
"""Build a Discriminator Model
|
||||
```
|
||||
|
||||
```py
|
||||
Stack of LeakyReLU-Conv2D to discriminate real from fake.
|
||||
The network does not converge with BN so it is not used here
|
||||
unlike in [1] or original paper.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
inputs (Layer): Input layer of the discriminator (the image)
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
discriminator (Model): Discriminator Model
|
||||
"""
|
||||
kernel_size = 5
|
||||
layer_filters = [32, 64, 128, 256]
|
||||
```
|
||||
|
||||
```py
|
||||
x = inputs
|
||||
for filters in layer_filters:
|
||||
# first 3 convolution layers use strides = 2
|
||||
# last one uses strides = 1
|
||||
if filters == layer_filters[-1]:
|
||||
strides = 1
|
||||
else:
|
||||
strides = 2
|
||||
x = LeakyReLU(alpha=0.2)(x)
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
x = Flatten()(x)
|
||||
x = Dense(1)(x)
|
||||
x = Activation('sigmoid')(x)
|
||||
discriminator = Model(inputs, x, name='discriminator')
|
||||
return discriminator
|
||||
```
|
||||
|
||||
在“列表 4.2.3”中,我们将说明如何构建 GAN 模型。 首先,建立鉴别模型,然后实例化生成器模型。 对抗性模型只是生成器和判别器组合在一起。 在许多 GAN 中,批大小为 64 似乎是最常见的。 网络参数显示在“列表 4.2.3”中。
|
||||
|
||||
“列表 4.2.3”:`dcgan-mnist-4.2.1.py`
|
||||
|
||||
建立 DCGAN 模型并调用训练例程的函数:
|
||||
|
||||
```py
|
||||
def build_and_train_models():
|
||||
# load MNIST dataset
|
||||
(x_train, _), (_, _) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
# reshape data for CNN as (28, 28, 1) and normalize
|
||||
image_size = x_train.shape[1]
|
||||
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
model_name = "dcgan_mnist"
|
||||
# network parameters
|
||||
# the latent or z vector is 100-dim
|
||||
latent_size = 100
|
||||
batch_size = 64
|
||||
train_steps = 40000
|
||||
lr = 2e-4
|
||||
decay = 6e-8
|
||||
input_shape = (image_size, image_size, 1)
|
||||
```
|
||||
|
||||
```py
|
||||
# build discriminator model
|
||||
inputs = Input(shape=input_shape, name='discriminator_input')
|
||||
discriminator = build_discriminator(inputs)
|
||||
# [1] or original paper uses Adam,
|
||||
# but discriminator converges easily with RMSprop
|
||||
optimizer = RMSprop(lr=lr, decay=decay)
|
||||
discriminator.compile(loss='binary_crossentropy',
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
discriminator.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# build generator model
|
||||
input_shape = (latent_size, )
|
||||
inputs = Input(shape=input_shape, name='z_input')
|
||||
generator = build_generator(inputs, image_size)
|
||||
generator.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# build adversarial model
|
||||
optimizer = RMSprop(lr=lr * 0.5, decay=decay * 0.5)
|
||||
# freeze the weights of discriminator during adversarial training
|
||||
discriminator.trainable = False
|
||||
# adversarial = generator + discriminator
|
||||
adversarial = Model(inputs,
|
||||
discriminator(generator(inputs)),
|
||||
name=model_name)
|
||||
adversarial.compile(loss='binary_crossentropy',
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
adversarial.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# train discriminator and adversarial networks
|
||||
models = (generator, discriminator, adversarial)
|
||||
params = (batch_size, latent_size, train_steps, model_name)
|
||||
train(models, x_train, params)
|
||||
```
|
||||
|
||||
从“列表 4.2.1”和“列表 4.2.2”中可以看出,DCGAN 模型很简单。 使它们难以构建的原因是,网络中的较小更改设计很容易破坏训练收敛。 例如,如果在判别器中使用批量归一化,或者如果生成器中的`strides = 2`传输到后面的 CNN 层,则 DCGAN 将无法收敛。
|
||||
|
||||
“列表 4.2.4”显示了专用于训练判别器和对抗网络的函数。 由于自定义训练,将不使用常规的`fit()`函数。 取而代之的是,调用`train_on_batch()`对给定的数据批量运行单个梯度更新。 然后通过对抗网络训练生成器。 训练首先从数据集中随机选择一批真实图像。 这被标记为实数(1.0)。 然后,生成器将生成一批伪图像。 这被标记为假(0.0)。 这两个批量是连接在一起的,用于训练判别器。
|
||||
|
||||
完成此操作后,生成器将生成一批新的伪图像,并将其标记为真实(1.0)。 这批将用于训练对抗网络。 交替训练这两个网络约 40,000 步。 定期将基于特定噪声向量生成的 MNIST 数字保存在文件系统中。 在最后的训练步骤中,网络已收敛。 生成器模型也保存在文件中,因此我们可以轻松地将训练后的模型重新用于未来的 MNIST 数字生成。 但是,仅保存生成器模型,因为这是该 DCGAN 在生成新 MNIST 数字时的有用部分。 例如,我们可以通过执行以下操作来生成新的和随机的 MNIST 数字:
|
||||
|
||||
```py
|
||||
python3 dcgan-mnist-4.2.1.py --generator=dcgan_mnist.h5
|
||||
```
|
||||
|
||||
“列表 4.2.4”:`dcgan-mnist-4.2.1.py`
|
||||
|
||||
训练判别器和对抗网络的函数:
|
||||
|
||||
```py
|
||||
def train(models, x_train, params):
|
||||
"""Train the Discriminator and Adversarial Networks
|
||||
```
|
||||
|
||||
```py
|
||||
Alternately train Discriminator and Adversarial networks by batch.
|
||||
Discriminator is trained first with properly real and fake images.
|
||||
Adversarial is trained next with fake images pretending to be real
|
||||
Generate sample images per save_interval.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
models (list): Generator, Discriminator, Adversarial models
|
||||
x_train (tensor): Train images
|
||||
params (list) : Networks parameters
|
||||
```
|
||||
|
||||
```py
|
||||
"""
|
||||
# the GAN component models
|
||||
generator, discriminator, adversarial = models
|
||||
# network parameters
|
||||
batch_size, latent_size, train_steps, model_name = params
|
||||
# the generator image is saved every 500 steps
|
||||
save_interval = 500
|
||||
# noise vector to see how the generator output evolves during training
|
||||
noise_input = np.random.uniform(-1.0, 1.0, size=[16, latent_size])
|
||||
# number of elements in train dataset
|
||||
train_size = x_train.shape[0]
|
||||
for i in range(train_steps):
|
||||
# train the discriminator for 1 batch
|
||||
# 1 batch of real (label=1.0) and fake images (label=0.0)
|
||||
# randomly pick real images from dataset
|
||||
rand_indexes = np.random.randint(0, train_size, size=batch_size)
|
||||
real_images = x_train[rand_indexes]
|
||||
# generate fake images from noise using generator
|
||||
# generate noise using uniform distribution
|
||||
noise = np.random.uniform(-1.0,
|
||||
1.0,
|
||||
size=[batch_size, latent_size])
|
||||
# generate fake images
|
||||
fake_images = generator.predict(noise)
|
||||
# real + fake images = 1 batch of train data
|
||||
x = np.concatenate((real_images, fake_images))
|
||||
# label real and fake images
|
||||
# real images label is 1.0
|
||||
y = np.ones([2 * batch_size, 1])
|
||||
# fake images label is 0.0
|
||||
y[batch_size:, :] = 0.0
|
||||
# train discriminator network, log the loss and accuracy
|
||||
loss, acc = discriminator.train_on_batch(x, y)
|
||||
log = "%d: [discriminator loss: %f, acc: %f]" % (i, loss, acc)
|
||||
```
|
||||
|
||||
```py
|
||||
# train the adversarial network for 1 batch
|
||||
# 1 batch of fake images with label=1.0
|
||||
# since the discriminator weights
|
||||
# are frozen in adversarial network
|
||||
# only the generator is trained
|
||||
# generate noise using uniform distribution
|
||||
noise = np.random.uniform(-1.0,
|
||||
1.0,
|
||||
size=[batch_size, latent_size])
|
||||
# label fake images as real or 1.0
|
||||
y = np.ones([batch_size, 1])
|
||||
# train the adversarial network
|
||||
# note that unlike in discriminator training,
|
||||
# we do not save the fake images in a variable
|
||||
# the fake images go to the discriminator input of the adversarial
|
||||
# for classification
|
||||
# log the loss and accuracy
|
||||
loss, acc = adversarial.train_on_batch(noise, y)
|
||||
log = "%s [adversarial loss: %f, acc: %f]" % (log, loss, acc)
|
||||
print(log)
|
||||
if (i + 1) % save_interval == 0:
|
||||
# plot generator images on a periodic basis
|
||||
plot_images(generator,
|
||||
noise_input=noise_input,
|
||||
show=False,
|
||||
step=(i + 1),
|
||||
model_name=model_name)
|
||||
```
|
||||
|
||||
```py
|
||||
# save the model after training the generator
|
||||
# the trained generator can be reloaded for
|
||||
# future MNIST digit generation
|
||||
generator.save(model_name + ".h5")
|
||||
```
|
||||
|
||||
“图 4.2.2”显示了生成器伪造图像根据训练步骤的演变。 生成器已经以 5,000 步的速度生成了可识别的图像。 非常像拥有一个知道如何绘制数字的智能体。 值得注意的是,某些数字从一种可识别的形式(例如,最后一行的第二列中的 8)变为另一种形式(例如,0)。 当训练收敛时,判别器损失接近 0.5,而对抗性损失接近 1.0,如下所示:
|
||||
|
||||
```py
|
||||
39997: [discriminator loss: 0.423329, acc: 0.796875] [adversarial loss:
|
||||
0.819355, acc: 0.484375]
|
||||
39998: [discriminator loss: 0.471747, acc: 0.773438] [adversarial loss:
|
||||
1.570030, acc: 0.203125]
|
||||
39999: [discriminator loss: 0.532917, acc: 0.742188] [adversarial loss:
|
||||
0.824350, acc: 0.453125]
|
||||
```
|
||||
|
||||
我们可以看到以下结果:
|
||||
|
||||

|
||||
|
||||
图 4.2.2:DCGAN 生成器在不同训练步骤生成的伪造图像
|
||||
|
||||
在本节中,由 DCGAN 生成的伪造图像是随机的。
|
||||
|
||||
生成器无法控制哪个特定数字。 没有机制可以请求生成器提供特定的数字。 这个问题可以通过称为 CGAN [4]的 GAN 变体来解决,我们将在下一部分中进行讨论。
|
||||
|
||||
# 3\. Conditional GAN
|
||||
|
||||
使用与上一节相同的 GAN ,会对生成器和判别器输入都施加一个条件。 条件是数字的一键向量形式。 这与要生成的图像(生成器)或分类为真实或伪造的图像(判别器)相关。 CGAN 模型显示在“图 4.3.1”中。
|
||||
|
||||
CGAN 与 DCGAN 相似,除了附加的单热向量输入。 对于生成器,单热标签在`Dense`层之前与潜向量连接在一起。 对于判别器,添加了新的`Dense`层。 新层用于处理单热向量并对其进行整形,以使其适合于与后续 CNN 层的另一个输入连接。
|
||||
|
||||

|
||||
|
||||
图 4.3.1:CGAN 模型与 DCGAN 相似,只不过是单热向量,用于调节生成器和判别器的输出
|
||||
|
||||
生成器学习从 100 维输入向量和指定位数生成伪图像。 判别器基于真实和伪图像及其对应的标签将真实图像与伪图像分类。
|
||||
|
||||
CGAN 的基础仍然与原始 GAN 原理相同,区别在于判别器和生成器的输入均以“一热”标签`y`为条件。
|
||||
|
||||
通过在“公式 4.1.1”和“公式 4.1.5”中合并此条件,判别器和生成器的损失函数在“公式 4.3.1”和“公式 4.3.2”中显示,分别为:
|
||||
|
||||
 (Equation 4.3.1)
|
||||
|
||||
 (Equation 4.3.2)
|
||||
|
||||
给定“图 4.3.2”,将损失函数写为:
|
||||
|
||||
 (Equation 4.3.3)
|
||||
|
||||
 (Equation 4.3.4)
|
||||
|
||||
判别器的新损失函数旨在最大程度地减少预测来自数据集的真实图像和来自生成器的假图像(给定单热点标签)的误差。“图 4.3.2”显示了如何训练判别器。
|
||||
|
||||

|
||||
|
||||
图 4.3.2:训练 CGAN 判别器类似于训练 GAN 判别器。 唯一的区别是,所生成的伪造品和数据集的真实图像均以其相应的“一键通”标签作为条件。
|
||||
|
||||
生成器的新损失函数可最大程度地减少对以指定的一幅热标签为条件的伪造图像进行鉴别的正确预测。 生成器学习如何在给定单热向量的情况下生成特定的 MNIST 数字,该数字可能使判别器蒙蔽。“图 4.3.3”显示了如何训练生成器。
|
||||
|
||||

|
||||
|
||||
图 4.3.3:通过对抗网络训练 CGAN 生成器类似于训练 GAN 生成器。 唯一的区别是,生成的伪造图像以“一热”标签为条件
|
||||
|
||||
“列表 4.3.1”突出显示了判别器模型中所需的微小更改。 该代码使用`Dense`层处理单热点向量,并将其与输入图像连接在一起。 修改了`Model`实例以用于图像和一键输入向量。
|
||||
|
||||
“列表 4.3.1”:`cgan-mnist-4.3.1.py`
|
||||
|
||||
突出显示了 DCGAN 中所做的更改:
|
||||
|
||||
```py
|
||||
def build_discriminator(inputs, labels, image_size):
|
||||
"""Build a Discriminator Model
|
||||
```
|
||||
|
||||
```py
|
||||
Inputs are concatenated after Dense layer.
|
||||
Stack of LeakyReLU-Conv2D to discriminate real from fake.
|
||||
The network does not converge with BN so it is not used here
|
||||
unlike in DCGAN paper.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
inputs (Layer): Input layer of the discriminator (the image)
|
||||
labels (Layer): Input layer for one-hot vector to condition
|
||||
the inputs
|
||||
image_size: Target size of one side (assuming square image)
|
||||
Returns:
|
||||
discriminator (Model): Discriminator Model
|
||||
"""
|
||||
kernel_size = 5
|
||||
layer_filters = [32, 64, 128, 256]
|
||||
```
|
||||
|
||||
```py
|
||||
x = inputs
|
||||
```
|
||||
|
||||
```py
|
||||
y = Dense(image_size * image_size)(labels)
|
||||
y = Reshape((image_size, image_size, 1))(y)
|
||||
x = concatenate([x, y])
|
||||
```
|
||||
|
||||
```py
|
||||
for filters in layer_filters:
|
||||
# first 3 convolution layers use strides = 2
|
||||
# last one uses strides = 1
|
||||
if filters == layer_filters[-1]:
|
||||
strides = 1
|
||||
else:
|
||||
strides = 2
|
||||
x = LeakyReLU(alpha=0.2)(x)
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
x = Flatten()(x)
|
||||
x = Dense(1)(x)
|
||||
x = Activation('sigmoid')(x)
|
||||
# input is conditioned by labels
|
||||
discriminator = Model([inputs, labels], x, name='discriminator')
|
||||
return discriminator
|
||||
```
|
||||
|
||||
以下“列表 4.3.2”突出显示了代码更改,以在生成器生成器函数中合并条件化单热标签。 对于`z`向量和单热向量输入,修改了`Model`实例。
|
||||
|
||||
“列表 4.3.2”:`cgan-mnist-4.3.1.py`
|
||||
|
||||
突出显示了 DCGAN 中所做的更改:
|
||||
|
||||
```py
|
||||
def build_generator(inputs, labels, image_size):
|
||||
"""Build a Generator Model
|
||||
Inputs are concatenated before Dense layer.
|
||||
Stack of BN-ReLU-Conv2DTranpose to generate fake images.
|
||||
Output activation is sigmoid instead of tanh in orig DCGAN.
|
||||
Sigmoid converges easily.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
inputs (Layer): Input layer of the generator (the z-vector)
|
||||
labels (Layer): Input layer for one-hot vector to condition the inputs
|
||||
image_size: Target size of one side (assuming square image)
|
||||
Returns:
|
||||
generator (Model): Generator Model
|
||||
"""
|
||||
image_resize = image_size // 4
|
||||
# network parameters
|
||||
kernel_size = 5
|
||||
layer_filters = [128, 64, 32, 1]
|
||||
```
|
||||
|
||||
```py
|
||||
x = concatenate([inputs, labels], axis=1)
|
||||
x = Dense(image_resize * image_resize * layer_filters[0])(x)
|
||||
x = Reshape((image_resize, image_resize, layer_filters[0]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
for filters in layer_filters:
|
||||
# first two convolution layers use strides = 2
|
||||
# the last two use strides = 1
|
||||
if filters > layer_filters[-2]:
|
||||
strides = 2
|
||||
else:
|
||||
strides = 1
|
||||
x = BatchNormalization()(x)
|
||||
x = Activation('relu')(x)
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
x = Activation('sigmoid')(x)
|
||||
# input is conditioned by labels
|
||||
generator = Model([inputs, labels], x, name='generator')
|
||||
return generator
|
||||
```
|
||||
|
||||
“列表 4.3.3”突出显示了在`train()`函数中所做的更改,以适应判别器和生成器的条件一热向量。 首先对 CGAN 判别器进行训练,以一批真实和伪造的数据为条件,这些数据以其各自的热门标签为条件。 然后,在给定单热标签条件假冒数据为假的情况下,通过训练对抗网络来更新生成器参数。 与 DCGAN 相似,在对抗训练中,判别器权重被冻结。
|
||||
|
||||
“列表 4.3.3”:`cgan-mnist-4.3.1.py`
|
||||
|
||||
着重介绍了 DCGAN 中所做的更改:
|
||||
|
||||
```py
|
||||
def train(models, data, params):
|
||||
"""Train the Discriminator and Adversarial Networks
|
||||
```
|
||||
|
||||
```py
|
||||
Alternately train Discriminator and Adversarial networks by batch.
|
||||
Discriminator is trained first with properly labelled real and fake images.
|
||||
Adversarial is trained next with fake images pretending to be real.
|
||||
Discriminator inputs are conditioned by train labels for real images,
|
||||
and random labels for fake images.
|
||||
Adversarial inputs are conditioned by random labels.
|
||||
Generate sample images per save_interval.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
models (list): Generator, Discriminator, Adversarial models
|
||||
data (list): x_train, y_train data
|
||||
params (list): Network parameters
|
||||
```
|
||||
|
||||
```py
|
||||
"""
|
||||
# the GAN models
|
||||
generator, discriminator, adversarial = models
|
||||
# images and labels
|
||||
x_train, y_train = data
|
||||
# network parameters
|
||||
batch_size, latent_size, train_steps, num_labels, model_name = params
|
||||
# the generator image is saved every 500 steps
|
||||
save_interval = 500
|
||||
# noise vector to see how the generator output evolves during training
|
||||
noise_input = np.random.uniform(-1.0, 1.0, size=[16, latent_size])
|
||||
# one-hot label the noise will be conditioned to
|
||||
noise_class = np.eye(num_labels)[np.arange(0, 16) % num_labels]
|
||||
# number of elements in train dataset
|
||||
train_size = x_train.shape[0]
|
||||
```
|
||||
|
||||
```py
|
||||
print(model_name,
|
||||
"Labels for generated images: ",
|
||||
np.argmax(noise_class, axis=1))
|
||||
```
|
||||
|
||||
```py
|
||||
for i in range(train_steps):
|
||||
# train the discriminator for 1 batch
|
||||
# 1 batch of real (label=1.0) and fake images (label=0.0)
|
||||
# randomly pick real images from dataset
|
||||
rand_indexes = np.random.randint(0, train_size, size=batch_size)
|
||||
real_images = x_train[rand_indexes]
|
||||
# corresponding one-hot labels of real images
|
||||
real_labels = y_train[rand_indexes]
|
||||
# generate fake images from noise using generator
|
||||
noise = np.random.uniform(-1.0,
|
||||
1.0,
|
||||
size=[batch_size, latent_size])
|
||||
```
|
||||
|
||||
```py
|
||||
# assign random one-hot labels
|
||||
fake_labels = np.eye(num_labels)[np.random.choice(num_labels,batch_size)]
|
||||
# generate fake images conditioned on fake labels
|
||||
fake_images = generator.predict([noise, fake_labels])
|
||||
# real + fake images = 1 batch of train data
|
||||
x = np.concatenate((real_images, fake_images))
|
||||
# real + fake one-hot labels = 1 batch of train one-hot labels
|
||||
labels = np.concatenate((real_labels, fake_labels))
|
||||
# label real and fake images
|
||||
# real images label is 1.0
|
||||
y = np.ones([2 * batch_size, 1])
|
||||
# fake images label is 0.0
|
||||
y[batch_size:, :] = 0.0
|
||||
# train discriminator network, log the loss and accuracy
|
||||
loss, acc = discriminator.train_on_batch([x, labels], y)
|
||||
log = "%d: [discriminator loss: %f, acc: %f]" % (i, loss, acc)
|
||||
# train the adversarial network for 1 batch
|
||||
# 1 batch of fake images conditioned on fake 1-hot labels
|
||||
# w/ label=1.0
|
||||
# since the discriminator weights are frozen in
|
||||
# adversarial network only the generator is trained
|
||||
# generate noise using uniform distribution
|
||||
noise = np.random.uniform(-1.0,
|
||||
1.0,
|
||||
size=[batch_size, latent_size])
|
||||
# assign random one-hot labels
|
||||
fake_labels = np.eye(num_labels)[np.random.choice(num_labels,batch_size)]
|
||||
```
|
||||
|
||||
```py
|
||||
# label fake images as real or 1.0
|
||||
y = np.ones([batch_size, 1])
|
||||
# train the adversarial network
|
||||
# note that unlike in discriminator training,
|
||||
# we do not save the fake images in a variable
|
||||
# the fake images go to the discriminator input of the adversarial
|
||||
# for classification
|
||||
# log the loss and accuracy
|
||||
loss, acc = adversarial.train_on_batch([noise, fake_labels], y)
|
||||
log = "%s [adversarial loss: %f, acc: %f]" % (log, loss, acc)
|
||||
print(log)
|
||||
if (i + 1) % save_interval == 0:
|
||||
# plot generator images on a periodic basis
|
||||
plot_images(generator,
|
||||
noise_input=noise_input,
|
||||
noise_class=noise_class,
|
||||
show=False,
|
||||
step=(i + 1),
|
||||
model_name=model_name)
|
||||
```
|
||||
|
||||
```py
|
||||
# save the model after training the generator
|
||||
# the trained generator can be reloaded for
|
||||
# future MNIST digit generation
|
||||
generator.save(model_name + ".h5")
|
||||
```
|
||||
|
||||
“图 4.3.4”显示了当生成器被调整为产生带有以下标签的数字时生成的 MNIST 数字的演变:
|
||||
|
||||
```py
|
||||
[0 1 2 3
|
||||
4 5 6 7
|
||||
8 9 0 1
|
||||
2 3 4 5]
|
||||
```
|
||||
|
||||
我们可以看到以下结果:
|
||||
|
||||

|
||||
|
||||
图 4.3.4:使用标签`[0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5]`对 CGAN 在不同训练步骤中生成的伪造图像
|
||||
|
||||
鼓励您运行经过训练的生成器模型,以查看新的合成 MNIST 数字图像:
|
||||
|
||||
```py
|
||||
python3 cgan-mnist-4.3.1.py --generator=cgan_mnist.h5
|
||||
```
|
||||
|
||||
或者,也可以请求要生成的特定数字(例如 8):
|
||||
|
||||
```py
|
||||
python3 cgan-mnist-4.3.1.py --generator=cgan_mnist.h5 --digit=8
|
||||
```
|
||||
|
||||
使用 CGAN,就像有一个智能体,我们可以要求绘制数字,类似于人类如何写数字。 与 DCGAN 相比,CGAN 的主要优势在于我们可以指定希望智能体绘制的数字。
|
||||
|
||||
# 4。结论
|
||||
|
||||
本章讨论了 GAN 的一般原理,以便为我们现在要讨论的更高级的主题奠定基础,包括改进的 GAN,解缠的表示 GAN 和跨域 GAN。 我们从了解 GAN 如何由两个网络(称为生成器和判别器)组成的这一章开始。 判别器的作用是区分真实信号和虚假信号。 生成器的目的是欺骗判别器。 生成器通常与判别器结合以形成对抗网络。 生成器是通过训练对抗网络来学习如何生成可欺骗判别器的虚假数据的。
|
||||
|
||||
我们还了解了 GAN 的构建方法,但众所周知,其操作起来非常困难。 提出了`tf.keras`中的两个示例实现。 DCGAN 证明了可以训练 GAN 使用深层 CNN 生成伪造图像。 伪造的图像是 MNIST 数字。 但是,DCGAN 生成器无法控制应绘制的特定数字。 CGAN 通过调节生成器以绘制特定数字来解决此问题。 该病是单热标签的形式。 如果我们要构建可以生成特定类数据的智能体,则 CGAN 很有用。
|
||||
|
||||
在下一章中,将介绍 DCGAN 和 CGAN 的改进。 特别是,重点将放在如何稳定 DCGAN 的训练以及如何提高 CGAN 的感知质量上。 这将通过引入新的损失函数和稍有不同的模型架构来完成。
|
||||
|
||||
# 5\. 参考
|
||||
|
||||
1. `Ian Goodfellow. NIPS 2016 Tutorial: Generative Adversarial Networks. arXiv preprint arXiv:1701.00160, 2016 (https://arxiv.org/pdf/1701.00160.pdf).`
|
||||
1. `Alec Radford, Luke Metz, and Soumith Chintala. Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks. arXiv preprint arXiv:1511.06434, 2015 (https://arxiv.org/pdf/1511.06434.pdf).`
|
||||
1. `Mehdi Mirza and Simon Osindero. Conditional Generative Adversarial Nets. arXiv preprint arXiv:1411.1784, 2014 (https://arxiv.org/pdf/1411.1784.pdf).`
|
||||
1. `Tero Karras et al. Progressive Growing of GANs for Improved Quality, Stability, and Variation. ICLR, 2018 (https://arxiv.org/pdf/1710.10196.pdf).`
|
||||
1. `Tero Karras, , Samuli Laine, and Timo Aila. A Style-Based Generator Architecture for Generative Adversarial Networks. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2019.`
|
||||
1. `Tero Karras et al. Analyzing and Improving the Image Quality of StyleGAN. 2019 (https://arxiv.org/abs/1912.04958).`
|
||||
@@ -1,990 +0,0 @@
|
||||
# 七、跨域 GAN
|
||||
|
||||
在计算机视觉,计算机图形学和图像处理中,许多任务涉及将图像从一种形式转换为另一种形式。 灰度图像的着色,将卫星图像转换为地图,将一位艺术家的艺术品风格更改为另一位艺术家,将夜间图像转换为白天,将夏季照片转换为冬天只是几个例子。 这些任务被称为**跨域迁移**,将成为本章的重点。 源域中的图像将迁移到目标域,从而生成新的转换图像。
|
||||
|
||||
跨域迁移在现实世界中具有许多实际应用。 例如,在自动驾驶研究中,收集公路现场驾驶数据既费时又昂贵。 为了在该示例中覆盖尽可能多的场景变化,将在不同的天气条件,季节和时间中遍历道路,从而为我们提供了大量不同的数据。 使用跨域迁移,可以通过转换现有图像来生成看起来真实的新合成场景。 例如,我们可能只需要在夏天从一个区域收集道路场景,在冬天从另一地方收集道路场景。 然后,我们可以将夏季图像转换为冬季,并将冬季图像转换为夏季。 在这种情况下,它将必须完成的任务数量减少了一半。
|
||||
|
||||
现实的合成图像的生成是 GAN 擅长的领域。 因此,跨域翻译是 GAN 的应用之一。 在本章中,我们将重点介绍一种流行的跨域 GAN 算法,称为 *CycleGAN* [2]。 与其他跨域迁移算法(例如 *pix2pix* [3])不同,CycleGAN 不需要对齐的训练图像即可工作。 在对齐的图像中,训练数据应该是由源图像及其对应的目标图像组成的一对图像; 例如,卫星图像和从该图像得出的相应地图。
|
||||
|
||||
CycleGAN 仅需要卫星数据图像和地图。 这些地图可以来自其他卫星数据,而不必事先从训练数据中生成。
|
||||
|
||||
在本章中,我们将探讨以下内容:
|
||||
|
||||
* CycleGAN 的原理,包括其在`tf.keras`中的实现
|
||||
* CycleGAN 的示例应用,包括使用 CIFAR10 数据集对灰度图像进行着色和应用于 MNIST 数字和*街景门牌号码(SVHN)* [1]数据集的样式迁移
|
||||
|
||||
让我们开始讨论 CycleGAN 背后的原理。
|
||||
|
||||
# 1\. CycleGAN 的原理
|
||||
|
||||
将图像从一个域转换到另一个域是计算机视觉,计算机图形学和图像处理中的常见任务。“图 7.1.1”显示了边缘检测,这是常见的图像转换任务:
|
||||
|
||||

|
||||
|
||||
图 7.1.1:对齐图像对的示例:使用 Canny 边缘检测器的左,原始图像和右,变换后的图像。 原始照片是作者拍摄的。
|
||||
|
||||
在此示例中,我们可以将真实照片(左)视为源域中的图像,将边缘检测的照片(右)视为目标域中的样本。 还有许多其他具有实际应用的跨域翻译过程,例如:
|
||||
|
||||
* 卫星图像到地图
|
||||
* 脸部图像到表情符号,漫画或动画
|
||||
* 身体图像到头像
|
||||
* 灰度照片的着色
|
||||
* 医学扫描到真实照片
|
||||
* 真实照片到画家的绘画
|
||||
|
||||
在不同领域中还有许多其他示例。 例如,在计算机视觉和图像处理中,我们可以通过发明一种从源图像中提取特征并将其转换为目标图像的算法来执行翻译。 坎尼边缘算子就是这种算法的一个例子。 但是,在很多情况下,翻译对于手工工程师而言非常复杂,因此几乎不可能找到合适的算法。 源域分布和目标域分布都是高维且复杂的。
|
||||
|
||||
解决图像翻译问题的一种方法是使用深度学习技术。 如果我们具有来自源域和目标域的足够大的数据集,则可以训练神经网络对转换进行建模。 由于必须在给定源图像的情况下自动生成目标域中的图像,因此它们必须看起来像是来自目标域的真实样本。 GAN 是适合此类跨域任务的网络。 *pix2pix* [3]算法是跨域算法的示例。
|
||||
|
||||
pix2pix 算法与**条件 GAN**(**CGAN**)[4]相似,我们在“第 4 章”,“生成对抗网络(GAN)”。 我们可以回想起在 CGAN 中,除了`z`噪声输入之外,诸如单热向量之类的条件会限制生成器的输出。 例如,在 MNIST 数字中,如果我们希望生成器输出数字 8,则条件为单热向量`[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]`。 在 pix2pix 中,条件是要翻译的图像。 生成器的输出是翻译后的图像。 通过优化 CGAN 损失来训练 pix2pix 算法。 为了使生成的图像中的模糊最小化,还包括 *L1* 损失。
|
||||
|
||||
类似于 pix2pix 的神经网络的主要缺点是训练输入和输出图像必须对齐。“图 7.1.1”是对齐的图像对的示例。 样本目标图像是从源生成的。 在大多数情况下,对齐的图像对不可用或无法从源图像生成,也不昂贵,或者我们不知道如何从给定的源图像生成目标图像。 我们拥有的是来自源域和目标域的样本数据。“图 7.1.2”是来自同一向日葵主题上源域(真实照片)和目标域(范高的艺术风格)的数据示例。 源图像和目标图像不一定对齐。
|
||||
|
||||
与 pix2pix 不同,CycleGAN 会学习图像翻译,只要源数据和目标数据之间有足够的数量和差异即可。 无需对齐。 CycleGAN 学习源和目标分布,以及如何从给定的样本数据中将源分布转换为目标分布。 无需监督。 在“图 7.1.2”的上下文中,我们只需要数千张真实向日葵的照片和数千张梵高向日葵画的照片。 在训练了 CycleGAN 之后,我们可以将向日葵的照片转换成梵高的画作:
|
||||
|
||||

|
||||
|
||||
图 7.1.2:未对齐的图像对示例:左侧为菲律宾大学沿着大学大道的真实向日葵照片,右侧为伦敦国家美术馆的梵高的向日葵, 英国。 原始照片由作者拍摄。
|
||||
|
||||
下一个问题是:我们如何建立可以从未配对数据中学习的模型? 在下一部分中,我们将构建一个使用正向和反向循环 GAN 的 CycleGAN,以及一个循环一致性检查,以消除对配对输入数据的需求。
|
||||
|
||||
## CycleGAN 模型
|
||||
|
||||
“图 7.1.3”显示了 CycleGAN 的网络模型:
|
||||
|
||||

|
||||
|
||||
图 7.1.3:CycleGAN 模型包含四个网络:生成器`G`,生成器`F`,判别器`D[y]`和判别器`D[x]`
|
||||
|
||||
让我们逐个讨论“图 7.1.3”。 让我们首先关注上层网络,即转发周期 GAN。 如下图“图 7.1.4”所示,正向循环 CycleGAN 的目标是学习以下函数:
|
||||
|
||||
 (Equation 7.1.1)
|
||||
|
||||

|
||||
|
||||
图 7.1.4:伪造`y`的 CycleGAN 生成器`G`
|
||||
|
||||
“公式 7.1.1”只是假目标数据`y'`的生成器`G`。 它将数据从源域`x`转换为目标域`y`。
|
||||
|
||||
要训练生成器,我们必须构建 GAN。 这是正向循环 GAN,如图“图 7.1.5”所示。 该图表明,它类似于“第 4 章”,“生成对抗网络(GANs)”中的典型 GAN,由生成器`G`和判别器`D[y]`组成,它可以以相同的对抗方式进行训练。通过仅利用源域中的可用实际图像`x`和目标域中的实际图像`y`,进行无监督学习。
|
||||
|
||||

|
||||
|
||||
图 7.1.5:CycleGAN 正向循环 GAN
|
||||
|
||||
与常规 GAN 不同,CycleGAN 施加了周期一致性约束,如图“图 7.1.6”所示。 前向循环一致性网络可确保可以从伪造的目标数据中重建真实的源数据:
|
||||
|
||||
 (Equation 7.1.2)
|
||||
|
||||

|
||||
|
||||
图 7.1.6:CycleGAN 循环一致性检查
|
||||
|
||||
通过最小化正向循环一致性 *L1* 损失来完成:
|
||||
|
||||
 (Equation 7.1.3)
|
||||
|
||||
周期一致性损失使用 *L1* 或**平均绝对误差**(**MAE**),因为与 *L2* 或**均方误差**(**MSE**)相比,它通常导致较少的模糊图像重建。
|
||||
|
||||
循环一致性检查表明,尽管我们已将源数据`x`转换为域`y`,但`x`的原始特征仍应保留在`y`中并且可恢复。 网络`F`只是我们将从反向循环 GAN 借用的另一个生成器,如下所述。
|
||||
|
||||
CycleGAN 是对称的。 如图“图 7.1.7”所示,后向循环 GAN 与前向循环 GAN 相同,但将源数据`x`和目标数据`y`的作用逆转。 现在,源数据为`y`,目标数据为`x`。 生成器`G`和`F`的作用也相反。`F`现在是生成器,而`G`恢复输入。 在正向循环 GAN 中,生成器`F`是用于恢复源数据的网络,而`G`是生成器。
|
||||
|
||||
Backward Cycle GAN 生成器的目标是合成:
|
||||
|
||||
 (Equation 7.1.2)
|
||||
|
||||

|
||||
|
||||
图 7.1.7:CycleGAN 向后循环 GAN
|
||||
|
||||
这可以通过对抗性训练反向循环 GAN 来完成。 目的是让生成器`F`学习如何欺骗判别器`D[x]`。
|
||||
|
||||
此外,还具有类似的向后循环一致性,以恢复原始源`y`:
|
||||
|
||||
 (Equation 7.1.4)
|
||||
|
||||
这是通过最小化后向循环一致性 *L1* 损失来完成的:
|
||||
|
||||
 (Equation 7.1.5)
|
||||
|
||||
总而言之,CycleGAN 的最终目标是使生成器`G`学习如何合成伪造的目标数据`y'`,该伪造的目标数据`y'`会在正向循环中欺骗识别器`D[y]`。 由于网络是对称的,因此 CycleGAN 还希望生成器`F`学习如何合成伪造的源数据`x'`,该伪造的源数据可以使判别器`D[x]`在反向循环中蒙蔽。 考虑到这一点,我们现在可以将所有损失函数放在一起。
|
||||
|
||||
让我们从 GAN 部分开始。 受到*最小二乘 GAN(LSGAN)* [5]更好的感知质量的启发,如“第 5 章”,“改进的 GAN” 中所述,CycleGAN 还使用 MSE 作为判别器和生成器损失。 回想一下,LSGAN 与原始 GAN 之间的差异需要使用 MSE 损失,而不是二进制交叉熵损失。
|
||||
|
||||
CycleGAN 将生成器-标识符损失函数表示为:
|
||||
|
||||
 (Equation 7.1.6)
|
||||
|
||||
 (Equation 7.1.7)
|
||||
|
||||
 (Equation 7.1.8)
|
||||
|
||||
 (Equation 7.1.9)
|
||||
|
||||
 (Equation 7.1.10)
|
||||
|
||||
 (Equation 7.1.11)
|
||||
|
||||
损失函数的第二组是周期一致性损失,可以通过汇总前向和后向 GAN 的贡献来得出:
|
||||
|
||||

|
||||
|
||||
 (Equation 7.1.12)
|
||||
|
||||
CycleGAN 的总损失为:
|
||||
|
||||
 (Equation 7.1.13)
|
||||
|
||||
CycleGAN 建议使用以下权重值`λ1 = 1.0`和`λ2 = 10.0`,以更加重视循环一致性检查。
|
||||
|
||||
训练策略类似于原始 GAN。 “算法 7.1.1”总结了 CycleGAN 训练过程。
|
||||
|
||||
“算法 7.1.1”:CycleGAN 训练
|
||||
|
||||
对`n`训练步骤重复上述步骤:
|
||||
|
||||
1. 通过使用真实的源数据和目标数据训练前向循环判别器,将`L_forward_GAN^(D)`降至最低。 实际目标数据的小批量`y`标记为 1.0。 伪造的目标数据`y' = G(x)`的小批量标记为 0.0。
|
||||
2. 通过使用真实的源数据和目标数据训练反向循环判别器,将`L_backward_GAN^(D)`最小化。 实际源数据的小批量`x`标记为 1.0。 一小部分伪造的源数据`x' = F(y)`被标记为 0.0。
|
||||
|
||||
1. 通过训练对抗网络中的前向周期和后向周期生成器,将`L_GAN^(D)`和`L_cyc`最小化。 伪造目标数据的一个小批量`y' = G(x)`被标记为 1.0。 一小部分伪造的源数据`x' = F(y)`被标记为 1.0。 判别器的权重被冻结。
|
||||
|
||||
在神经样式迁移问题中,颜色组合可能无法成功地从源图像迁移到伪造目标图像。 此问题显示在“图 7.1.8”中:
|
||||
|
||||

|
||||
|
||||
图 7.1.8:在样式迁移过程中,颜色组合可能无法成功迁移。 为了解决此问题,将恒等损失添加到总损失函数中
|
||||
|
||||
为了解决这个问题,CycleGAN 建议包括正向和反向循环身份损失函数:
|
||||
|
||||
 (Equation 7.1.14)
|
||||
|
||||
CycleGAN 的总损失变为:
|
||||
|
||||
 (Equation 7.1.15)
|
||||
|
||||
其中`λ3 = 0.5`。 在对抗训练中,身份损失也得到了优化。“图 7.1.9”重点介绍了实现身份正则器的 CycleGAN 辅助网络:
|
||||
|
||||

|
||||
|
||||
图 7.1.9:具有身份正则化网络的 CycleGAN 模型,图像左侧突出显示
|
||||
|
||||
在下一个部分,我们将在`tf.keras`中实现 CycleGAN。
|
||||
|
||||
## 使用 Keras 实现 CycleGAN
|
||||
|
||||
我们来解决,这是 CycleGAN 可以解决的简单问题。 在“第 3 章”,“自编码器”中,我们使用了自编码器为 CIFAR10 数据集中的灰度图像着色。 我们可以记得,CIFAR10 数据集包含 50,000 个训练过的数据项和 10,000 个测试数据样本,这些样本属于 10 个类别的`32 x 32` RGB 图像。 我们可以使用`rgb2gray`(RGB)将所有彩色图像转换为灰度图像,如“第 3 章”,“自编码器”中所述。
|
||||
|
||||
接下来,我们可以将灰度训练图像用作源域图像,将原始彩色图像用作目标域图像。 值得注意的是,尽管数据集是对齐的,但我们 CycleGAN 的输入是彩色图像的随机样本和灰度图像的随机样本。 因此,我们的 CycleGAN 将看不到训练数据对齐。 训练后,我们将使用测试的灰度图像来观察 CycleGAN 的表现。
|
||||
|
||||
如前几节所述,要实现 CycleGAN,我们需要构建两个生成器和两个判别器。 CycleGAN 的生成器学习源输入分布的潜在表示,并将该表示转换为目标输出分布。 这正是自编码器的功能。 但是,类似于“第 3 章”,“自编码器”中讨论的典型自编码器,使用的编码器会对输入进行下采样,直到瓶颈层为止,此时解码器中的处理过程相反。
|
||||
|
||||
由于在编码器和解码器层之间共享许多低级特征,因此该结构不适用于某些图像转换问题。 例如,在着色问题中,灰度图像的形式,结构和边缘与彩色图像中的相同。 为了解决这个问题,CycleGAN 生成器使用 *U-Net* [7]结构,如图“图 7.1.10”所示:
|
||||
|
||||

|
||||
|
||||
图 7.1.10:在 Keras 中实现正向循环生成器`G`。 产生器是包括编码器和解码器的 U 网络[7]。
|
||||
|
||||
在 U-Net 结构中,编码器层的输出`e[ni]`与解码器层的输出`d[i]`,其中`n = 4`是编码器/解码器的层数,`i = 1, 2, 3`是共享信息的层号。
|
||||
|
||||
我们应该注意,尽管该示例使用`n = 4`,但输入/输出尺寸较大的问题可能需要更深的编码器/解码器层。 通过 U-Net 结构,可以在编码器和解码器之间自由迁移特征级别的信息。
|
||||
|
||||
编码器层由`Instance Normalization(IN)-LeakyReLU-Conv2D`组成,而解码器层由`IN-ReLU-Conv2D`组成。 编码器/解码器层的实现如清单 7.1.1 所示,而生成器的实现如列表 7.1.2 所示。
|
||||
|
||||
[完整的代码可在 GitHub 上找到](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
**实例规范化**(**IN**)是每个数据(即 IN 是图像或每个特征的 BN)。 在样式迁移中,重要的是标准化每个样本而不是每个批量的对比度。 IN 等于,相当于对比度归一化。 同时,BN 打破了对比度标准化。
|
||||
|
||||
记住在使用 IN 之前先安装`tensorflow-addons`:
|
||||
|
||||
```py
|
||||
$ pip install tensorflow-addons
|
||||
```
|
||||
|
||||
“列表 7.1.1”:`cyclegan-7.1.1.py`
|
||||
|
||||
```py
|
||||
def encoder_layer(inputs,
|
||||
filters=16,
|
||||
kernel_size=3,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
instance_norm=True):
|
||||
"""Builds a generic encoder layer made of Conv2D-IN-LeakyReLU
|
||||
IN is optional, LeakyReLU may be replaced by ReLU
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
conv = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')
|
||||
```
|
||||
|
||||
```py
|
||||
x = inputs
|
||||
if instance_norm:
|
||||
x = InstanceNormalization(axis=3)(x)
|
||||
if activation == 'relu':
|
||||
x = Activation('relu')(x)
|
||||
else:
|
||||
x = LeakyReLU(alpha=0.2)(x)
|
||||
x = conv(x)
|
||||
return x
|
||||
```
|
||||
|
||||
```py
|
||||
def decoder_layer(inputs,
|
||||
paired_inputs,
|
||||
filters=16,
|
||||
kernel_size=3,
|
||||
strides=2,
|
||||
activation='relu',
|
||||
instance_norm=True):
|
||||
"""Builds a generic decoder layer made of Conv2D-IN-LeakyReLU
|
||||
IN is optional, LeakyReLU may be replaced by ReLU
|
||||
Arguments: (partial)
|
||||
inputs (tensor): the decoder layer input
|
||||
paired_inputs (tensor): the encoder layer output
|
||||
provided by U-Net skip connection &
|
||||
concatenated to inputs.
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
conv = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
strides=strides,
|
||||
padding='same')
|
||||
```
|
||||
|
||||
```py
|
||||
x = inputs
|
||||
if instance_norm:
|
||||
x = InstanceNormalization(axis=3)(x)
|
||||
if activation == 'relu':
|
||||
x = Activation('relu')(x)
|
||||
else:
|
||||
x = LeakyReLU(alpha=0.2)(x)
|
||||
x = conv(x)
|
||||
x = concatenate([x, paired_inputs])
|
||||
return x
|
||||
```
|
||||
|
||||
将移至生成器实现中:
|
||||
|
||||
“列表 7.1.2”:`cyclegan-7.1.1.py`
|
||||
|
||||
Keras 中的生成器实现:
|
||||
|
||||
```py
|
||||
def build_generator(input_shape,
|
||||
output_shape=None,
|
||||
kernel_size=3,
|
||||
name=None):
|
||||
"""The generator is a U-Network made of a 4-layer encoder
|
||||
and a 4-layer decoder. Layer n-i is connected to layer i.
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
input_shape (tuple): input shape
|
||||
output_shape (tuple): output shape
|
||||
kernel_size (int): kernel size of encoder & decoder layers
|
||||
name (string): name assigned to generator model
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
generator (Model):
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
inputs = Input(shape=input_shape)
|
||||
channels = int(output_shape[-1])
|
||||
e1 = encoder_layer(inputs,
|
||||
32,
|
||||
kernel_size=kernel_size,
|
||||
activation='leaky_relu',
|
||||
strides=1)
|
||||
e2 = encoder_layer(e1,
|
||||
64,
|
||||
activation='leaky_relu',
|
||||
kernel_size=kernel_size)
|
||||
e3 = encoder_layer(e2,
|
||||
128,
|
||||
activation='leaky_relu',
|
||||
kernel_size=kernel_size)
|
||||
e4 = encoder_layer(e3,
|
||||
256,
|
||||
activation='leaky_relu',
|
||||
kernel_size=kernel_size)
|
||||
```
|
||||
|
||||
```py
|
||||
d1 = decoder_layer(e4,
|
||||
e3,
|
||||
128,
|
||||
kernel_size=kernel_size)
|
||||
d2 = decoder_layer(d1,
|
||||
e2,
|
||||
64,
|
||||
kernel_size=kernel_size)
|
||||
d3 = decoder_layer(d2,
|
||||
e1,
|
||||
32,
|
||||
kernel_size=kernel_size)
|
||||
outputs = Conv2DTranspose(channels,
|
||||
kernel_size=kernel_size,
|
||||
strides=1,
|
||||
activation='sigmoid',
|
||||
padding='same')(d3)
|
||||
```
|
||||
|
||||
```py
|
||||
generator = Model(inputs, outputs, name=name)
|
||||
```
|
||||
|
||||
```py
|
||||
return generator
|
||||
```
|
||||
|
||||
CycleGAN 的判别器类似于原始 GAN 判别器。 输入图像被下采样数次(在此示例中为 3 次)。 最后一层是`Dense`(1)层,它预测输入为实数的可能性。 除了不使用 IN 之外,每个层都类似于生成器的编码器层。 然而,在大图像中,用一个数字将图像计算为真实图像或伪图像会导致参数效率低下,并导致生成器的图像质量较差。
|
||||
|
||||
解决方案是使用 PatchGAN [6],该方法将图像划分为补丁网格,并使用标量值网格来预测补丁是真实概率。“图 7.1.11”显示了原始 GAN 判别器和`2 x 2` PatchGAN 判别器之间的比较:
|
||||
|
||||

|
||||
|
||||
图 7.1.11:GAN 与 PatchGAN 判别器的比较
|
||||
|
||||
在此示例中,面片不重叠且在其边界处相遇。 但是,通常,补丁可能会重叠。
|
||||
|
||||
我们应该注意,PatchGAN 并没有在 CycleGAN 中引入一种新型的 GAN。 为了提高生成的图像质量,如果使用`2 x 2` PatchGAN,则没有四个输出可以区分,而没有一个输出可以区分。 损失函数没有变化。 从直觉上讲,这是有道理的,因为如果图像的每个面片或部分看起来都是真实的,则整个图像看起来会更加真实。
|
||||
|
||||
“图 7.1.12”显示了`tf.keras`中实现的判别器网络。 下图显示了判别器确定输入图像或色块为彩色 CIFAR10 图像的可能性:
|
||||
|
||||

|
||||
|
||||
图 7.1.12:目标标识符`D[y]`在`tf.keras`中的实现。 PatchGAN 判别器显示在右侧
|
||||
|
||||
由于输出图像只有`32 x 32` RGB 时较小,因此表示该图像是真实的单个标量就足够了。 但是,当使用 PatchGAN 时,我们也会评估结果。“列表 7.1.3”显示了判别器的函数构建器:
|
||||
|
||||
“列表 7.1.3”:`cyclegan-7.1.1.py`
|
||||
|
||||
`tf.keras`中的判别器实现:
|
||||
|
||||
```py
|
||||
def build_discriminator(input_shape,
|
||||
kernel_size=3,
|
||||
patchgan=True,
|
||||
name=None):
|
||||
"""The discriminator is a 4-layer encoder that outputs either
|
||||
a 1-dim or a n x n-dim patch of probability that input is real
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
input_shape (tuple): input shape
|
||||
kernel_size (int): kernel size of decoder layers
|
||||
patchgan (bool): whether the output is a patch
|
||||
or just a 1-dim
|
||||
name (string): name assigned to discriminator model
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
discriminator (Model):
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
inputs = Input(shape=input_shape)
|
||||
x = encoder_layer(inputs,
|
||||
32,
|
||||
kernel_size=kernel_size,
|
||||
activation='leaky_relu',
|
||||
instance_norm=False)
|
||||
x = encoder_layer(x,
|
||||
64,
|
||||
kernel_size=kernel_size,
|
||||
activation='leaky_relu',
|
||||
instance_norm=False)
|
||||
x = encoder_layer(x,
|
||||
128,
|
||||
kernel_size=kernel_size,
|
||||
activation='leaky_relu',
|
||||
instance_norm=False)
|
||||
x = encoder_layer(x,
|
||||
256,
|
||||
kernel_size=kernel_size,
|
||||
strides=1,
|
||||
activation='leaky_relu',
|
||||
instance_norm=False)
|
||||
```
|
||||
|
||||
```py
|
||||
# if patchgan=True use nxn-dim output of probability
|
||||
# else use 1-dim output of probability
|
||||
if patchgan:
|
||||
x = LeakyReLU(alpha=0.2)(x)
|
||||
outputs = Conv2D(1,
|
||||
kernel_size=kernel_size,
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
else:
|
||||
x = Flatten()(x)
|
||||
x = Dense(1)(x)
|
||||
outputs = Activation('linear')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
discriminator = Model(inputs, outputs, name=name)
|
||||
```
|
||||
|
||||
```py
|
||||
return discriminator
|
||||
```
|
||||
|
||||
使用生成器和判别器生成器,我们现在可以构建 CycleGAN。“列表 7.1.4”显示了构建器函数。 与上一节中的讨论一致,实例化了两个生成器`g_source = F`和`g_target = G`以及两个判别器`d_source = D[x]`和`d_target = D[y]`。 正向循环为`x' = F(G(x)) = reco_source = g_source(g_target(source_input))`。反向循环为`y' = G(F(y)) = reco_target = g_target(g_source (target_input))`。
|
||||
|
||||
对抗模型的输入是源数据和目标数据,而输出是`D[x]`和`D[y]`的输出以及重构的输入`x'`和`y'`。 在本示例中,由于由于灰度图像和彩色图像中通道数之间的差异,因此未使用身份网络。 对于 GAN 和循环一致性损失,我们分别使用建议的`λ1 = 1.0`和`λ2 = 10.0`损失权重。 与前几章中的 GAN 相似,我们使用 RMSprop 作为判别器的优化器,其学习率为`2e-4`,衰减率为`6e-8`。 对抗的学习率和衰退率是判别器的一半。
|
||||
|
||||
“列表 7.1.4”:`cyclegan-7.1.1.py`
|
||||
|
||||
`tf.keras`中的 CycleGAN 构建器:
|
||||
|
||||
```py
|
||||
def build_cyclegan(shapes,
|
||||
source_name='source',
|
||||
target_name='target',
|
||||
kernel_size=3,
|
||||
patchgan=False,
|
||||
identity=False
|
||||
):
|
||||
"""Build the CycleGAN
|
||||
```
|
||||
|
||||
```py
|
||||
1) Build target and source discriminators
|
||||
2) Build target and source generators
|
||||
3) Build the adversarial network
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
shapes (tuple): source and target shapes
|
||||
source_name (string): string to be appended on dis/gen models
|
||||
target_name (string): string to be appended on dis/gen models
|
||||
kernel_size (int): kernel size for the encoder/decoder
|
||||
or dis/gen models
|
||||
patchgan (bool): whether to use patchgan on discriminator
|
||||
identity (bool): whether to use identity loss
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
(list): 2 generator, 2 discriminator,
|
||||
and 1 adversarial models
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
source_shape, target_shape = shapes
|
||||
lr = 2e-4
|
||||
decay = 6e-8
|
||||
gt_name = "gen_" + target_name
|
||||
gs_name = "gen_" + source_name
|
||||
dt_name = "dis_" + target_name
|
||||
ds_name = "dis_" + source_name
|
||||
```
|
||||
|
||||
```py
|
||||
# build target and source generators
|
||||
g_target = build_generator(source_shape,
|
||||
target_shape,
|
||||
kernel_size=kernel_size,
|
||||
name=gt_name)
|
||||
g_source = build_generator(target_shape,
|
||||
source_shape,
|
||||
kernel_size=kernel_size,
|
||||
name=gs_name)
|
||||
print('---- TARGET GENERATOR ----')
|
||||
g_target.summary()
|
||||
print('---- SOURCE GENERATOR ----')
|
||||
g_source.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
# build target and source discriminators
|
||||
d_target = build_discriminator(target_shape,
|
||||
patchgan=patchgan,
|
||||
kernel_size=kernel_size,
|
||||
name=dt_name)
|
||||
d_source = build_discriminator(source_shape,
|
||||
patchgan=patchgan,
|
||||
kernel_size=kernel_size,
|
||||
name=ds_name)
|
||||
print('---- TARGET DISCRIMINATOR ----')
|
||||
d_target.summary()
|
||||
print('---- SOURCE DISCRIMINATOR ----')
|
||||
d_source.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
optimizer = RMSprop(lr=lr, decay=decay)
|
||||
d_target.compile(loss='mse',
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
d_source.compile(loss='mse',
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
```
|
||||
|
||||
```py
|
||||
d_target.trainable = False
|
||||
d_source.trainable = False
|
||||
```
|
||||
|
||||
```py
|
||||
# build the computational graph for the adversarial model
|
||||
# forward cycle network and target discriminator
|
||||
source_input = Input(shape=source_shape)
|
||||
fake_target = g_target(source_input)
|
||||
preal_target = d_target(fake_target)
|
||||
reco_source = g_source(fake_target)
|
||||
```
|
||||
|
||||
```py
|
||||
# backward cycle network and source discriminator
|
||||
target_input = Input(shape=target_shape)
|
||||
fake_source = g_source(target_input)
|
||||
preal_source = d_source(fake_source)
|
||||
reco_target = g_target(fake_source)
|
||||
```
|
||||
|
||||
```py
|
||||
# if we use identity loss, add 2 extra loss terms
|
||||
# and outputs
|
||||
if identity:
|
||||
iden_source = g_source(source_input)
|
||||
iden_target = g_target(target_input)
|
||||
loss = ['mse', 'mse', 'mae', 'mae', 'mae', 'mae']
|
||||
loss_weights = [1., 1., 10., 10., 0.5, 0.5]
|
||||
inputs = [source_input, target_input]
|
||||
outputs = [preal_source,
|
||||
preal_target,
|
||||
reco_source,
|
||||
reco_target,
|
||||
iden_source,
|
||||
iden_target]
|
||||
else:
|
||||
loss = ['mse', 'mse', 'mae', 'mae']
|
||||
loss_weights = [1., 1., 10., 10.]
|
||||
inputs = [source_input, target_input]
|
||||
outputs = [preal_source,
|
||||
preal_target,
|
||||
reco_source,
|
||||
reco_target]
|
||||
```
|
||||
|
||||
```py
|
||||
# build adversarial model
|
||||
adv = Model(inputs, outputs, name='adversarial')
|
||||
optimizer = RMSprop(lr=lr*0.5, decay=decay*0.5)
|
||||
adv.compile(loss=loss,
|
||||
loss_weights=loss_weights,
|
||||
optimizer=optimizer,
|
||||
metrics=['accuracy'])
|
||||
print('---- ADVERSARIAL NETWORK ----')
|
||||
adv.summary()
|
||||
```
|
||||
|
||||
```py
|
||||
return g_source, g_target, d_source, d_target, adv
|
||||
```
|
||||
|
||||
我们遵循训练过程,我们可以从上一节中的“算法 7.1.1”中调用。“列表 7.1.5”显示了 CycleGAN 训练。 此训练与原始 GAN 之间的次要区别是有两个要优化的判别器。 但是,只有一种对抗模型需要优化。 对于每 2,000 步,生成器将保存预测的源图像和目标图像。 我们将的批量大小设为 32。我们也尝试了 1 的批量大小,但是输出质量几乎相同,并且需要花费更长的时间进行训练(批量为每个图像 43 ms,在 NVIDIA GTX 1060 上批量大小为 32 时,最大大小为每个图像 1 vs 3.6 ms)
|
||||
|
||||
“列表 7.1.5”:`cyclegan-7.1.1.py`
|
||||
|
||||
`tf.keras`中的 CycleGAN 训练例程:
|
||||
|
||||
```py
|
||||
def train_cyclegan(models,
|
||||
data,
|
||||
params,
|
||||
test_params,
|
||||
test_generator):
|
||||
""" Trains the CycleGAN.
|
||||
|
||||
1) Train the target discriminator
|
||||
2) Train the source discriminator
|
||||
3) Train the forward and backward cyles of
|
||||
adversarial networks
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
models (Models): Source/Target Discriminator/Generator,
|
||||
Adversarial Model
|
||||
data (tuple): source and target training data
|
||||
params (tuple): network parameters
|
||||
test_params (tuple): test parameters
|
||||
test_generator (function): used for generating
|
||||
predicted target and source images
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
# the models
|
||||
g_source, g_target, d_source, d_target, adv = models
|
||||
# network parameters
|
||||
batch_size, train_steps, patch, model_name = params
|
||||
# train dataset
|
||||
source_data, target_data, test_source_data, test_target_data\
|
||||
= data
|
||||
```
|
||||
|
||||
```py
|
||||
titles, dirs = test_params
|
||||
```
|
||||
|
||||
```py
|
||||
# the generator image is saved every 2000 steps
|
||||
save_interval = 2000
|
||||
target_size = target_data.shape[0]
|
||||
source_size = source_data.shape[0]
|
||||
```
|
||||
|
||||
```py
|
||||
# whether to use patchgan or not
|
||||
if patch > 1:
|
||||
d_patch = (patch, patch, 1)
|
||||
valid = np.ones((batch_size,) + d_patch)
|
||||
fake = np.zeros((batch_size,) + d_patch)
|
||||
else:
|
||||
valid = np.ones([batch_size, 1])
|
||||
fake = np.zeros([batch_size, 1])
|
||||
```
|
||||
|
||||
```py
|
||||
valid_fake = np.concatenate((valid, fake))
|
||||
start_time = datetime.datetime.now()
|
||||
```
|
||||
|
||||
```py
|
||||
for step in range(train_steps):
|
||||
# sample a batch of real target data
|
||||
rand_indexes = np.random.randint(0,
|
||||
target_size,
|
||||
size=batch_size)
|
||||
real_target = target_data[rand_indexes]
|
||||
```
|
||||
|
||||
```py
|
||||
# sample a batch of real source data
|
||||
rand_indexes = np.random.randint(0,
|
||||
source_size,
|
||||
size=batch_size)
|
||||
real_source = source_data[rand_indexes]
|
||||
# generate a batch of fake target data fr real source data
|
||||
fake_target = g_target.predict(real_source)
|
||||
```
|
||||
|
||||
```py
|
||||
# combine real and fake into one batch
|
||||
x = np.concatenate((real_target, fake_target))
|
||||
# train the target discriminator using fake/real data
|
||||
metrics = d_target.train_on_batch(x, valid_fake)
|
||||
log = "%d: [d_target loss: %f]" % (step, metrics[0])
|
||||
```
|
||||
|
||||
```py
|
||||
# generate a batch of fake source data fr real target data
|
||||
fake_source = g_source.predict(real_target)
|
||||
x = np.concatenate((real_source, fake_source))
|
||||
# train the source discriminator using fake/real data
|
||||
metrics = d_source.train_on_batch(x, valid_fake)
|
||||
log = "%s [d_source loss: %f]" % (log, metrics[0])
|
||||
```
|
||||
|
||||
```py
|
||||
# train the adversarial network using forward and backward
|
||||
# cycles. the generated fake source and target
|
||||
# data attempts to trick the discriminators
|
||||
x = [real_source, real_target]
|
||||
y = [valid, valid, real_source, real_target]
|
||||
metrics = adv.train_on_batch(x, y)
|
||||
elapsed_time = datetime.datetime.now() - start_time
|
||||
fmt = "%s [adv loss: %f] [time: %s]"
|
||||
log = fmt % (log, metrics[0], elapsed_time)
|
||||
print(log)
|
||||
if (step + 1) % save_interval == 0:
|
||||
test_generator((g_source, g_target),
|
||||
(test_source_data, test_target_data),
|
||||
step=step+1,
|
||||
titles=titles,
|
||||
dirs=dirs,
|
||||
show=False)
|
||||
```
|
||||
|
||||
```py
|
||||
# save the models after training the generators
|
||||
g_source.save(model_name + "-g_source.h5")
|
||||
g_target.save(model_name + "-g_target.h5")
|
||||
```
|
||||
|
||||
最后,在使用 CycleGAN 构建和训练函数之前,我们必须执行一些数据准备。 模块`cifar10_utils.py`和`other_ utils.py`加载`CIFAR10`训练和测试数据。 有关这两个文件的详细信息,请参考源代码。 加载后,将训练图像和测试图像转换为灰度,以生成源数据和测试源数据。
|
||||
|
||||
“列表 7.1.6”显示了 CycleGAN 如何用于构建和训练用于灰度图像着色的生成器网络(`g_target`)。 由于 CycleGAN 是对称的,因此我们还构建并训练了第二个生成器网络(`g_source`),该网络可以将颜色转换为灰度。 训练了两个 CycleGAN 着色网络。 第一种使用标量输出类似于原始 GAN 的判别器,第二种使用`2 x 2` PatchGAN。
|
||||
|
||||
“列表 7.1.6”:`cyclegan-7.1.1.py`
|
||||
|
||||
CycleGAN 用于着色:
|
||||
|
||||
```py
|
||||
def graycifar10_cross_colorcifar10(g_models=None):
|
||||
"""Build and train a CycleGAN that can do
|
||||
grayscale <--> color cifar10 images
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
model_name = 'cyclegan_cifar10'
|
||||
batch_size = 32
|
||||
train_steps = 100000
|
||||
patchgan = True
|
||||
kernel_size = 3
|
||||
postfix = ('%dp' % kernel_size) \
|
||||
if patchgan else ('%d' % kernel_size)
|
||||
```
|
||||
|
||||
```py
|
||||
data, shapes = cifar10_utils.load_data()
|
||||
source_data, _, test_source_data, test_target_data = data
|
||||
titles = ('CIFAR10 predicted source images.',
|
||||
'CIFAR10 predicted target images.',
|
||||
'CIFAR10 reconstructed source images.',
|
||||
'CIFAR10 reconstructed target images.')
|
||||
dirs = ('cifar10_source-%s' % postfix, \
|
||||
'cifar10_target-%s' % postfix)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate predicted target(color) and source(gray) images
|
||||
if g_models is not None:
|
||||
g_source, g_target = g_models
|
||||
other_utils.test_generator((g_source, g_target),
|
||||
(test_source_data, \
|
||||
test_target_data),
|
||||
step=0,
|
||||
titles=titles,
|
||||
dirs=dirs,
|
||||
show=True)
|
||||
return
|
||||
```
|
||||
|
||||
```py
|
||||
# build the cyclegan for cifar10 colorization
|
||||
models = build_cyclegan(shapes,
|
||||
"gray-%s" % postfix,
|
||||
"color-%s" % postfix,
|
||||
kernel_size=kernel_size,
|
||||
patchgan=patchgan)
|
||||
# patch size is divided by 2^n since we downscaled the input
|
||||
# in the discriminator by 2^n (ie. we use strides=2 n times)
|
||||
patch = int(source_data.shape[1] / 2**4) if patchgan else 1
|
||||
params = (batch_size, train_steps, patch, model_name)
|
||||
test_params = (titles, dirs)
|
||||
# train the cyclegan
|
||||
train_cyclegan(models,
|
||||
data,
|
||||
params,
|
||||
test_params,
|
||||
other_utils.test_generator)
|
||||
```
|
||||
|
||||
在的下一部分中,我们将检查 CycleGAN 的生成器输出以进行着色。
|
||||
|
||||
## CycleGAN 的生成器输出
|
||||
|
||||
“图 7.1.13”显示 CycleGAN 的着色结果。 源图像来自测试数据集:
|
||||
|
||||

|
||||
|
||||
图 7.1.13:使用不同技术进行着色。 显示的是基本事实,使用自编码器的着色(第 3 章,自编码器),使用带有原始 GAN 判别器的 CycleGAN 进行着色,以及使用带有 PatchGAN 判别器的 CycleGAN 进行着色。 彩色效果最佳。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
为了进行比较,我们使用第 3 章,“自编码器”中描述的普通自编码器显示了地面真实情况和着色结果。 通常,所有彩色图像在感觉上都是可接受的。 总体而言,似乎每种着色技术都有自己的优点和缺点。 所有着色方法与天空和车辆的正确颜色不一致。
|
||||
|
||||
例如,平面背景(第三行,第二列)中的天空为白色。 自编码器没错,但是 CycleGAN 认为它是浅棕色或蓝色。
|
||||
|
||||
对于第六行第六列,暗海上的船天空阴沉,但自编码器将其涂成蓝色和蓝色,而 CycleGAN 将其涂成蓝色和白色,而没有 PatchGAN。 两种预测在现实世界中都是有意义的。 同时,使用 PatchGAN 对 CycleGAN 的预测与基本事实相似。 在倒数第二行和第二列上,没有方法能够预测汽车的红色。 在动物身上,CycleGAN 的两种口味都具有接近真实情况的颜色。
|
||||
|
||||
由于 CycleGAN 是对称的,因此它还能在给定彩色图像的情况下预测灰度图像。“图 7.1.14”显示了两个 CycleGAN 变体执行的颜色到灰度转换。 目标图像来自测试数据集。 除了某些图像的灰度阴影存在细微差异外,这些预测通常是准确的。
|
||||
|
||||

|
||||
|
||||
图 7.1.14:颜色(来自图 7.1.9)到 CycleGAN 的灰度转换
|
||||
|
||||
要训练 CycleGAN 进行着色,命令是:
|
||||
|
||||
```py
|
||||
python3 cyclegan-7.1.1.py -c
|
||||
```
|
||||
|
||||
读者可以使用带有 PatchGAN 的 CycleGAN 预训练模型来运行图像转换:
|
||||
|
||||
```py
|
||||
python3 cyclegan-7.1.1.py --cifar10_g_source=cyclegan_cifar10-g_source.h5
|
||||
--cifar10_g_target=cyclegan_cifar10-g_target.h5
|
||||
```
|
||||
|
||||
在本节中,我们演示了 CycleGAN 在着色上的一种实际应用。 在下一部分中,我们将在更具挑战性的数据集上训练 CycleGAN。 源域 MNIST 与目标域 SVHN 数据集有很大的不同[1]。
|
||||
|
||||
## MNIST 和 SVHN 数据集上的 CycleGAN
|
||||
|
||||
我们现在要解决一个更具挑战性的问题。 假设我们使用 MNIST 灰度数字作为源数据,并且我们想从 *SVHN* [1]中借鉴样式,这是我们的目标数据。 每个域中的样本数据显示在“图 7.1.15”中:
|
||||
|
||||

|
||||
|
||||
图 7.1.15:两个未对齐数据的不同域。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
我们可以重用上一节中讨论的 CycleGAN 的所有构建和训练函数,以执行样式迁移。 唯一的区别是,我们必须添加用于加载 MNIST 和 SVHN 数据的例程。 SVHN 数据集可在[这个页面](http://ufldl.stanford.edu/housenumbers/)中找到。
|
||||
|
||||
我们介绍`mnist_svhn_utils.py`模块来帮助我们完成此任务。“列表 7.1.7”显示了针对跨域迁移的 CycleGAN 的初始化和训练。
|
||||
|
||||
CycleGAN 结构与上一部分相同,不同之处在于我们使用的核大小为 5,因为两个域完全不同。
|
||||
|
||||
“列表 7.1.7”:`cyclegan-7.1.1.py`
|
||||
|
||||
CycleGAN 用于 MNIST 和 SVHN 之间的跨域样式迁移:
|
||||
|
||||
```py
|
||||
def mnist_cross_svhn(g_models=None):
|
||||
"""Build and train a CycleGAN that can do mnist <--> svhn
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
model_name = 'cyclegan_mnist_svhn'
|
||||
batch_size = 32
|
||||
train_steps = 100000
|
||||
patchgan = True
|
||||
kernel_size = 5
|
||||
postfix = ('%dp' % kernel_size) \
|
||||
if patchgan else ('%d' % kernel_size)
|
||||
```
|
||||
|
||||
```py
|
||||
data, shapes = mnist_svhn_utils.load_data()
|
||||
source_data, _, test_source_data, test_target_data = data
|
||||
titles = ('MNIST predicted source images.',
|
||||
'SVHN predicted target images.',
|
||||
'MNIST reconstructed source images.',
|
||||
'SVHN reconstructed target images.')
|
||||
dirs = ('mnist_source-%s' \
|
||||
% postfix, 'svhn_target-%s' % postfix)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate predicted target(svhn) and source(mnist) images
|
||||
if g_models is not None:
|
||||
g_source, g_target = g_models
|
||||
other_utils.test_generator((g_source, g_target),
|
||||
(test_source_data, \
|
||||
test_target_data),
|
||||
step=0,
|
||||
titles=titles,
|
||||
dirs=dirs,
|
||||
show=True)
|
||||
return
|
||||
```
|
||||
|
||||
```py
|
||||
# build the cyclegan for mnist cross svhn
|
||||
models = build_cyclegan(shapes,
|
||||
"mnist-%s" % postfix,
|
||||
"svhn-%s" % postfix,
|
||||
kernel_size=kernel_size,
|
||||
patchgan=patchgan)
|
||||
# patch size is divided by 2^n since we downscaled the input
|
||||
# in the discriminator by 2^n (ie. we use strides=2 n times)
|
||||
patch = int(source_data.shape[1] / 2**4) if patchgan else 1
|
||||
params = (batch_size, train_steps, patch, model_name)
|
||||
test_params = (titles, dirs)
|
||||
# train the cyclegan
|
||||
train_cyclegan(models,
|
||||
data,
|
||||
params,
|
||||
test_params,
|
||||
other_utils.test_generator)
|
||||
```
|
||||
|
||||
将 MNIST 从测试数据集迁移到 SVHN 的结果显示在“图 7.1.16”中。 生成的图像具有样式的 SVHN,但是数字未完全传送。 例如,在第四行上,数字 3、1 和 3 由 CycleGAN 进行样式化。
|
||||
|
||||
但是,在第三行中,不带有和带有 PatchGAN 的 CycleGAN 的数字 9、6 和 6 分别设置为 0、6、01、0、65 和 68:
|
||||
|
||||

|
||||
|
||||
图 7.1.16:测试数据从 MNIST 域到 SVHN 的样式迁移。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
向后循环的结果为“图 7.1.17”中所示的。 在这种情况下,目标图像来自 SVHN 测试数据集。 生成的图像具有 MNIST 的样式,但是数字没有正确翻译。 例如,在第一行中,对于不带和带有 PatchGAN 的 CycleGAN,数字 5、2 和 210 分别被样式化为 7、7、8、3、3 和 1:
|
||||
|
||||

|
||||
|
||||
图 7.1.17:测试数据从 SVHN 域到 MNIST 的样式迁移。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
在 PatchGAN 的情况下,假设预测的 MNIST 数字被限制为一位,则输出 1 是可以理解的。 有以某种方式正确的预测,例如在第二行中,不使用 PatchGAN 的 CycleGAN 将 SVHN 数字的最后三列 6、3 和 4 转换为 6、3 和 6。 但是,CycleGAN 两种版本的输出始终是个位数且可识别。
|
||||
|
||||
从 MNIST 到 SVHN 的转换中出现的问题称为“标签翻转”[8],其中源域中的数字转换为目标域中的另一个数字。 尽管 CycleGAN 的预测是周期一致的,但它们不一定是语义一致的。 在翻译过程中数字的含义会丢失。
|
||||
|
||||
为了解决这个问题, *Hoffman* [8]引入了一种改进的 CycleGAN,称为**循环一致性对抗域自适应**(**CyCADA**)。 不同之处在于,附加的语义损失项可确保预测不仅周期一致,而且语义一致。
|
||||
|
||||
“图 7.1.18”显示 CycleGAN 在正向循环中重建 MNIST 数字。 重建的 MNIST 数字几乎与源 MNIST 数字相同:
|
||||
|
||||

|
||||
|
||||
图 7.1.18:带有 MNIST 上的 PatchGAN 的 CycleGAN(源)到 SVHN(目标)的前向周期。 重建的源类似于原始源。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
“图 7.1.19”显示了 CycleGAN 在向后周期中重构 SVHN 数字的过程:
|
||||
|
||||

|
||||
|
||||
图 7.1.19:带有 MNIST 上的 PatchGAN 的 CycleGAN 与 SVHN(目标)的反向循环。 重建的目标与原始目标并不完全相似。 原始彩色照片可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/blob/master/chapter7-cross-domain-gan/README.md)中找到。
|
||||
|
||||
在“图 7.1.3”中,CycleGAN 被描述为具有周期一致性。 换句话说,给定源`x`,CycleGAN 将正向循环中的源重构为`x'`。 另外,在给定目标`y`的情况下,CycleGAN 在反向循环中将目标重构为`y'`。
|
||||
|
||||
重建了许多目标图像。 有些数字显然是相同的,例如最后两列(3 和 4)中的第二行,而有些数字却是相同的但是模糊的,例如前两列列中的第一行(5 和 2)。 尽管样式仍像第二行一样,但在前两列(从 33 和 6 到 1 以及无法识别的数字)中,有些数字会转换为另一数字。
|
||||
|
||||
要将 MNIST 的 CycleGAN 训练为 SVHN,命令为:
|
||||
|
||||
```py
|
||||
python3 cyclegan-7.1.1.py -m
|
||||
```
|
||||
|
||||
鼓励读者使用带有 PatchGAN 的 CycleGAN 预训练模型来运行图像翻译:
|
||||
|
||||
```py
|
||||
python3 cyclegan-7.1.1.py --mnist_svhn_g_source=cyclegan_mnist_svhn-g_ source.h5 --mnist_svhn_g_target=cyclegan_mnist_svhn-g_target.h5
|
||||
```
|
||||
|
||||
到目前为止,我们只看到了 CycleGAN 的两个实际应用。 两者都在小型数据集上进行了演示,以强调可重复性的概念。 如本章前面所述,CycleGAN 还有许多其他实际应用。 我们在这里介绍的 CycleGAN 可以作为分辨率更高的图像转换的基础。
|
||||
|
||||
# 2\. 总结
|
||||
|
||||
在本章中,我们讨论了 CycleGAN 作为可用于图像翻译的算法。 在 CycleGAN 中,源数据和目标数据不一定要对齐。 我们展示了两个示例,*灰度 ↔ 颜色*和 *MNIST ↔ SVHN* ,尽管 CycleGAN 可以执行许多其他可能的图像转换 。
|
||||
|
||||
在下一章中,我们将着手另一种生成模型,即**变分自编码器**(**VAE**)。 VAE 具有类似的学习目标–如何生成新图像(数据)。 他们专注于学习建模为高斯分布的潜在向量。 我们将以有条件的 VAE 和解开 VAE 中的潜在表示形式来证明 GAN 解决的问题中的其他相似之处。
|
||||
|
||||
# 3\. 参考
|
||||
|
||||
1. `Yuval Netzer et al.: Reading Digits in Natural Images with Unsupervised Feature Learning. NIPS workshop on deep learning and unsupervised feature learning. Vol. 2011. No. 2. 2011 (https://www-cs.stanford.edu/~twangcat/papers/nips2011_housenumbers.pdf).`
|
||||
1. `Zhu-Jun-Yan et al.: Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks. 2017 IEEE International Conference on Computer Vision (ICCV). IEEE, 2017 (http://openaccess.thecvf.com/content_ICCV_2017/papers/Zhu_Unpaired_Image-To-Image_Translation_ICCV_2017_paper.pdf).`
|
||||
1. `Phillip Isola et al.: Image-to-Image Translation with Conditional Adversarial Networks. 2017 IEEE Conference on Computer Vision and Pattern Recognition (CVPR). IEEE, 2017 (http://openaccess.thecvf.com/content_cvpr_2017/papers/Isola_Image-To-Image_Translation_With_CVPR_2017_paper.pdf).`
|
||||
1. `Mehdi Mirza and Simon Osindero. Conditional Generative Adversarial Nets. arXiv preprint arXiv:1411.1784, 2014 (https://arxiv.org/pdf/1411.1784.pdf).`
|
||||
1. `Xudong Mao et al.: Least Squares Generative Adversarial Networks. 2017 IEEE International Conference on Computer Vision (ICCV). IEEE, 2017 (http://openaccess.thecvf.com/content_ICCV_2017/papers/Mao_Least_Squares_Generative_ICCV_2017_paper.pdf).`
|
||||
1. `Chuan Li and Michael Wand. Precomputed Real-Time Texture Synthesis with Markovian Generative Adversarial Networks. European Conference on Computer Vision. Springer, Cham, 2016 (https://arxiv.org/pdf/1604.04382.pdf).`
|
||||
1. `Olaf Ronneberger, Philipp Fischer, and Thomas Brox. U-Net: Convolutional Networks for Biomedical Image Segmentation. International Conference on Medical image computing and computer-assisted intervention. Springer, Cham, 2015 (https://arxiv.org/pdf/1505.04597.pdf).`
|
||||
1. `Judy Hoffman et al.: CyCADA: Cycle-Consistent Adversarial Domain Adaptation. arXiv preprint arXiv:1711.03213, 2017 (https://arxiv.org/pdf/1711.03213.pdf).`
|
||||
@@ -1,699 +0,0 @@
|
||||
# 八、变分自编码器(VAE)
|
||||
|
||||
与我们在之前的章节中讨论过的**生成对抗网络**(**GAN**)类似,**变分自编码器**(**VAE**)[1] 属于生成模型家族。 VAE 的生成器能够在导航其连续潜在空间的同时产生有意义的输出。 通过潜向量探索解码器输出的可能属性。
|
||||
|
||||
在 GAN 中,重点在于如何得出近似输入分布的模型。 VAE 尝试对可解码的连续潜在空间中的输入分布进行建模。 这是 GAN 与 VAE 相比能够生成更真实信号的可能的潜在原因之一。 例如,在图像生成中,GAN 可以生成看起来更逼真的图像,而相比之下,VAE 生成的图像清晰度较差。
|
||||
|
||||
在 VAE 中,重点在于潜在代码的变分推理。 因此,VAE 为潜在变量的学习和有效贝叶斯推理提供了合适的框架。 例如,带有解缠结表示的 VAE 可以将潜在代码重用于迁移学习。
|
||||
|
||||
在结构上,VAE 与自编码器相似。 它也由编码器(也称为识别或推理模型)和解码器(也称为生成模型)组成。 VAE 和自编码器都试图在学习潜向量的同时重建输入数据。
|
||||
|
||||
但是,与自编码器不同,VAE 的潜在空间是连续的,并且解码器本身被用作生成模型。
|
||||
|
||||
在前面各章中讨论的 GAN 讨论中,也可以对 VAE 的解码器进行调整。 例如,在 MNIST 数据集中,我们能够指定一个给定的单热向量产生的数字。 这种有条件的 VAE 类别称为 CVAE [2]。 也可以通过在损失函数中包含正则化超参数来解开 VAE 潜向量。 这称为 β-VAE [5]。 例如,在 MNIST 中,我们能够隔离确定每个数字的粗细或倾斜角度的潜向量。 本章的目的是介绍:
|
||||
|
||||
* VAE 的原理
|
||||
* 了解重新参数化技巧,有助于在 VAE 优化中使用随机梯度下降
|
||||
* 有条件的 VAE(CVAE)和 β-VAE 的原理
|
||||
* 了解如何使用`tf.keras`实现 VAE
|
||||
|
||||
我们将从谈论 VAE 的基本原理开始。
|
||||
|
||||
# 1\. VAE 原理
|
||||
|
||||
在生成模型中,我们经常对使用神经网络来逼近输入的真实分布感兴趣:
|
||||
|
||||
 (Equation 8.1.1)
|
||||
|
||||
在前面的等式中,`θ`表示训练期间确定的参数。 例如,在名人面孔数据集的上下文中,这等效于找到可以绘制面孔的分布。 同样,在 MNIST 数据集中,此分布可以生成可识别的手写数字。
|
||||
|
||||
在机器学习中,为了执行特定级别的推理,我们有兴趣寻找`P[θ](x, z)`,这是输入`x`和潜在变量`z`之间的联合分布。 潜在变量不是数据集的一部分,而是对可从输入中观察到的某些属性进行编码。 在名人面孔的背景下,这些可能是面部表情,发型,头发颜色,性别等。 在 MNIST 数据集中,潜在变量可以表示数字和书写样式。
|
||||
|
||||
`P[θ](x, z)`实际上是输入数据点及其属性的分布。 `P[θ](x)`可以从边际分布计算得出:
|
||||
|
||||
 (Equation 8.1.2)
|
||||
|
||||
换句话说,考虑所有可能的属性,我们最终得到描述输入的分布。 在名人面孔中,如果考虑所有面部表情,发型,头发颜色和性别,将恢复描述名人面孔的分布。 在 MNIST 数据集中,如果考虑所有可能的数字,书写风格等,我们以手写数字的分布来结束。
|
||||
|
||||
问题在于“公式 8.1.2”很难处理。 该方程式没有解析形式或有效的估计量。 它的参数无法微分。 因此,通过神经网络进行优化是不可行的。
|
||||
|
||||
使用贝叶斯定理,我们可以找到“公式 8.1.2”的替代表达式:
|
||||
|
||||
 (Equation 8.1.3)
|
||||
|
||||
`P(z)`是`z`的先验分布。 它不以任何观察为条件。 如果`z`是离散的,而`P[θ](x | z)`是高斯分布,则`P[θ](x)`是高斯的混合。 如果`z`是连续的,则`P[θ](x)`是高斯的无限混合。
|
||||
|
||||
实际上,如果我们尝试在没有合适的损失函数的情况下建立一个近似`P[θ](x | z)`的神经网络,它将忽略`z`得出一个简单的解`P[θ](x | z) = P[θ](x)`。 因此,“公式 8.1.3”无法为我们提供`P[θ](x)`的良好估计。 或者,“公式 8.1.2”也可以表示为:
|
||||
|
||||
 (Equation 8.1.4)
|
||||
|
||||
但是,`P[θ](z | x)`也很棘手。 VAE 的目标是在给定输入的情况下,找到一种可预测的分布,该分布易于估计`P[θ](z | x)`,即潜在属性`z`的条件分布的估计。
|
||||
|
||||
## 变分推理
|
||||
|
||||
为了使易于处理,VAE 引入了变化推理模型(编码器):
|
||||
|
||||
 (Equation 8.1.5)
|
||||
|
||||
`Q[φ](z | x)`提供了`P[θ](z | x)`的良好估计。 它既参数化又易于处理。 `Q[φ](z | x)`可以通过优化参数`φ`由深度神经网络近似。 通常,`Q[φ](z | x)`被选择为多元高斯:
|
||||
|
||||
 (Equation 8.1.6)
|
||||
|
||||
均值`μ(x)`和标准差`σ(x)`均由编码器神经网络使用输入数据点计算得出。 对角线矩阵表示`z`的元素是独立的。
|
||||
|
||||
在下一节中,我们将求解 VAE 的核心方程。 核心方程式将引导我们找到一种优化算法,该算法将帮助我们确定推理模型的参数。
|
||||
|
||||
## 核心方程
|
||||
|
||||
推理模型`Q[φ](z | x)`从输入`x`生成潜向量`z`。 `Q[φ](z | x)`似于自编码器模型中的编码器。 另一方面,从潜在代码`z`重构输入。 `P[θ](x | z)`的作用类似于自编码器模型中的解码器。 要估计`P[θ](x)`,我们必须确定其与`Q[φ](z | x)`和`P[θ](x | z)`的关系。
|
||||
|
||||
如果`Q[φ](z | x)`是`P[θ](z | x)`的估计值,则 **Kullback-Leibler**(**KL**)的差异决定了这两个条件密度之间的距离:
|
||||
|
||||
 (Equation 8.1.7)
|
||||
|
||||
使用贝叶斯定理:
|
||||
|
||||
 (Equation 8.1.8)
|
||||
|
||||
在“公式 8.1.7”中:
|
||||
|
||||
 (Equation 8.1.9)
|
||||
|
||||
由于`log P[θ](x)`不依赖于`z ~ Q`,因此可能会超出预期。 重新排列“公式 8.1.9”并认识到:
|
||||
|
||||
,其结果是:
|
||||
|
||||
 (Equation 8.1.10)
|
||||
|
||||
“公式 8.1.10”是 VAE 的核心。 左侧是项`P[θ](x)`,由于`Q[φ](z | x)`与真实`P[θ](z | x)`的距离,我们使误差最小化。 我们可以记得,的对数不会更改最大值(或最小值)的位置。 给定提供`P[θ](z | x)`良好估计的推断模型,`D[KL](Q[φ](z | x) || P[θ](z | x)`大约为零。
|
||||
|
||||
右边的第一项`P[θ](x | z)`类似于解码器,该解码器从推理模型中抽取样本以重建输入。
|
||||
|
||||
第二个项是另一个距离。 这次是在`Q[φ](z | x)`和先前的`P[θ](z)`之间。 “公式 8.1.10”的左侧也称为**变异下界**或**证据下界**(**ELBO**)。 由于 KL 始终为正,因此 ELBO 是`log P[θ](x)`的下限。 通过优化神经网络的参数`φ`和`θ`来最大化 ELBO 意味着:
|
||||
|
||||
* 在将`z`中的`x`属性编码时,`D[KL](Q[φ](z | x) || P[θ](z | x) -> 0`或推理模型变得更好。
|
||||
* 右边的`log P[θ](x | z)`最大化了“公式 8.1.10”或解码器模型在从潜在向量`z`重构`x`方面变得更好。
|
||||
* 在下一节中,我们将利用“公式 8.1.10”的结构来确定推理模型(编码器)和解码器的损失函数。
|
||||
|
||||
## 优化
|
||||
|
||||
“公式 8.1.10”的右侧具有有关 VAE 的`loss`函数的两个重要信息。 解码器项`E[z~Q] [log P[θ](x | z)]`表示生成器从推理模型的输出中提取`z`个样本,以重建输入。 使最大化是指我们将**重构损失**和`L_R`降到最低。 如果假设图像(数据)分布为高斯分布,则可以使用 MSE。
|
||||
|
||||
如果每个像素(数据)都被认为是伯努利分布,那么损失函数就是二进制互熵。
|
||||
|
||||
第二项`-D[KL](Q[φ](z | x) || P[θ](z))`易于评估。 根据“公式 8.1.6”,`Q[φ]`是高斯分布。 通常,`P[θ](z) = P(z) = N(0, 1)`也是平均值为零且标准差等于 1.0 的高斯。 在“公式 8.1.11”中,我们看到 KL 项简化为:
|
||||
|
||||
 (Equation 8.1.11)
|
||||
|
||||
其中`J`是`z`的维。 `μ[j]`和`σ[j]`都是通过推理模型计算的`x`的函数。 要最大化:`-D[KL]`,`σ[j] -> 1`和`μ[j] -> 9`。 `P(z) = N(0, 1)`的选择源于各向同性单位高斯的性质,在具有适当函数的情况下,它可以变形为任意分布[6]。
|
||||
|
||||
根据“公式 8.1.11”,KL 损失`L_KL`简称为`D[KL]`。
|
||||
|
||||
总之,在“公式 8.1.12”中将 VAE `loss`函数定义为:
|
||||
|
||||
 (Equation 8.1.12)
|
||||
|
||||
在给定编码器和解码器模型的情况下,在我们可以构建和训练 VAE(随机采样块,生成潜在属性)之前,还需要解决一个问题。 在下一节中,我们将讨论此问题以及如何使用重新参数化技巧解决它。
|
||||
|
||||
## 重新参数化技巧
|
||||
|
||||
“图 8.1.1”的左侧显示了 VAE 网络。 编码器获取输入`x`,并估计潜向量`z`的多元高斯分布的平均值`μ`和标准差`σ`。 解码器从潜向量`z`中提取样本,以将输入重构为`x_tilde`。 这似乎很简单,直到在反向传播期间发生梯度更新为止:
|
||||
|
||||

|
||||
|
||||
图 8.1.1:带有和不带有重新参数化技巧的 VAE 网络
|
||||
|
||||
反向传播梯度将不会通过随机**采样**块。 尽管具有用于神经网络的随机输入是可以的,但梯度不可能穿过随机层。
|
||||
|
||||
解决此问题的方法是将**采样**处理作为输入,如“图 8.1.1”右侧所示。 然后,将样本计算为:
|
||||
|
||||
 (Equation 8.1.13)
|
||||
|
||||
如果`ε`和`σ`以向量格式表示,则`εσ`是逐元素乘法。 使用“公式 8.1.13”,看起来好像采样直接来自潜在空间一样。 这项技术被称为*重新参数化技巧*。
|
||||
|
||||
现在,在输入端发生*采样*时,可以使用熟悉的优化算法(例如 SGD,Adam 或 RMSProp)来训练 VAE 网络。
|
||||
|
||||
在讨论如何在`tf.keras`中实现 VAE 之前,让我们首先展示如何测试经过训练的解码器。
|
||||
|
||||
## 解码器测试
|
||||
|
||||
在训练了 VAE 网络之后,可以丢弃推理模型,包括加法和乘法运算符。 为了生成新的有意义的输出,请从用于生成`ε`的高斯分布中抽取样本。“图 8.1.2”向我们展示了解码器的测试设置:
|
||||
|
||||

|
||||
|
||||
图 8.1.2:解码器测试设置
|
||||
|
||||
通过重新参数化技巧解决了 VAE 上的最后一个问题,我们现在可以在`tf.keras`中实现和训练变分自编码器。
|
||||
|
||||
## ALAS 与 Keras
|
||||
|
||||
VAE 的结构类似于典型的自编码器。 区别主要在于重新参数化技巧中的高斯随机变量的采样。“列表 8.1.1”显示了使用 MLP 实现的编码器,解码器和 VAE。
|
||||
|
||||
[此代码也已添加到官方 Keras GitHub 存储库中](https://github.com/keras-team/keras/blob/master/examples/variational_autoencoder.py)。
|
||||
|
||||
为便于显示潜在代码,将`z`的维设置为 2。编码器仅是两层 MLP,第二层生成均值和对数方差。 对数方差的使用是为了简化 KL 损失和重新参数化技巧的计算。 编码器的第三个输出是使用重新参数化技巧进行的`z`采样。 我们应该注意,在采样函数`exp(0.5 log σ²) = sqrt(σ²) = σ`中,因为`σ > 0`假定它是高斯分布的标准差。
|
||||
|
||||
解码器也是两层 MLP,它采用`z`的样本来近似输入。 编码器和解码器均使用大小为 512 的中间尺寸。
|
||||
|
||||
VAE 网络只是将编码器和解码器连接在一起。 `loss`函数是*重建损失*和 *KL 损失*的总和。 在默认的 Adam 优化器上,VAE 网络具有良好的效果。 VAE 网络中的参数总数为 807,700。
|
||||
|
||||
VAE MLP 的 Keras 代码具有预训练的权重。 要测试,我们需要运行:
|
||||
|
||||
```py
|
||||
python3 vae-mlp-mnist-8.1.1.py --weights=vae_mlp_mnist.tf
|
||||
```
|
||||
|
||||
[完整的代码可以在以下链接中找到](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras)。
|
||||
|
||||
“列表 8.1.1”:`vae-mlp-mnist-8.1.1.py`
|
||||
|
||||
```py
|
||||
# reparameterization trick
|
||||
# instead of sampling from Q(z|X), sample eps = N(0,I)
|
||||
# z = z_mean + sqrt(var)*eps
|
||||
def sampling(args):
|
||||
"""Reparameterization trick by sampling
|
||||
fr an isotropic unit Gaussian.
|
||||
```
|
||||
|
||||
```py
|
||||
# Arguments:
|
||||
args (tensor): mean and log of variance of Q(z|X)
|
||||
```
|
||||
|
||||
```py
|
||||
# Returns:
|
||||
z (tensor): sampled latent vector
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
z_mean, z_log_var = args
|
||||
# K is the keras backend
|
||||
batch = K.shape(z_mean)[0]
|
||||
dim = K.int_shape(z_mean)[1]
|
||||
# by default, random_normal has mean=0 and std=1.0
|
||||
epsilon = K.random_normal(shape=(batch, dim))
|
||||
return z_mean + K.exp(0.5 * z_log_var) * epsilon
|
||||
```
|
||||
|
||||
```py
|
||||
# MNIST dataset
|
||||
(x_train, y_train), (x_test, y_test) = mnist.load_data()
|
||||
```
|
||||
|
||||
```py
|
||||
image_size = x_train.shape[1]
|
||||
original_dim = image_size * image_size
|
||||
x_train = np.reshape(x_train, [-1, original_dim])
|
||||
x_test = np.reshape(x_test, [-1, original_dim])
|
||||
x_train = x_train.astype('float32') / 255
|
||||
x_test = x_test.astype('float32') / 255
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (original_dim, )
|
||||
intermediate_dim = 512
|
||||
batch_size = 128
|
||||
latent_dim = 2
|
||||
epochs = 50
|
||||
```
|
||||
|
||||
```py
|
||||
# VAE model = encoder + decoder
|
||||
# build encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
x = Dense(intermediate_dim, activation='relu')(inputs)
|
||||
z_mean = Dense(latent_dim, name='z_mean')(x)
|
||||
z_log_var = Dense(latent_dim, name='z_log_var')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# use reparameterization trick to push the sampling out as input
|
||||
# note that "output_shape" isn't necessary
|
||||
# with the TensorFlow backend
|
||||
z = Lambda(sampling,
|
||||
output_shape=(latent_dim,),
|
||||
name='z')([z_mean, z_log_var])
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model(inputs, [z_mean, z_log_var, z], name='encoder')
|
||||
```
|
||||
|
||||
```py
|
||||
# build decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
|
||||
x = Dense(intermediate_dim, activation='relu')(latent_inputs)
|
||||
outputs = Dense(original_dim, activation='sigmoid')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model(latent_inputs, outputs, name='decoder')
|
||||
# instantiate VAE model
|
||||
outputs = decoder(encoder(inputs)[2])
|
||||
vae = Model(inputs, outputs, name='vae_mlp')
|
||||
```
|
||||
|
||||
```py
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
help_ = "Load tf model trained weights"
|
||||
parser.add_argument("-w", "--weights", help=help_)
|
||||
help_ = "Use binary cross entropy instead of mse (default)"
|
||||
parser.add_argument("--bce", help=help_, action='store_true')
|
||||
args = parser.parse_args()
|
||||
models = (encoder, decoder)
|
||||
data = (x_test, y_test)
|
||||
```
|
||||
|
||||
```py
|
||||
# VAE loss = mse_loss or xent_loss + kl_loss
|
||||
if args.bce:
|
||||
reconstruction_loss = binary_crossentropy(inputs,
|
||||
outputs)
|
||||
else:
|
||||
reconstruction_loss = mse(inputs, outputs)
|
||||
|
||||
reconstruction_loss *= original_dim
|
||||
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
|
||||
kl_loss = K.sum(kl_loss, axis=-1)
|
||||
kl_loss *= -0.5
|
||||
vae_loss = K.mean(reconstruction_loss + kl_loss)
|
||||
vae.add_loss(vae_loss)
|
||||
vae.compile(optimizer='adam')
|
||||
```
|
||||
|
||||
“图 8.1.3”显示了编码器模型,它是一个 MLP,具有两个输出,即潜向量的均值和方差。 lambda 函数实现了重新参数化技巧,将随机潜在代码的采样推送到 VAE 网络之外:
|
||||
|
||||

|
||||
|
||||
图 8.1.3:VAE MLP 的编码器模型
|
||||
|
||||
“图 8.1.4”显示了解码器模型。 2 维输入来自 lambda 函数。 输出是重构的 MNIST 数字:
|
||||
|
||||

|
||||
|
||||
图 8.1.4:VAE MLP 的解码器模型
|
||||
|
||||
“图 8.1.5”显示了完整的 VAE 模型。 通过将编码器和解码器模型结合在一起制成:
|
||||
|
||||

|
||||
|
||||
图 8.1.5:使用 MLP 的 VAE 模型
|
||||
|
||||
“图 8.1.6”显示了使用`plot_results()`在 50 个周期后潜向量的连续空间。 为简单起见,此函数未在此处显示,但可以在`vae-mlp-mnist-8.1.1.py`的其余代码中找到。 该函数绘制两个图像,即测试数据集标签(“图 8.1.6”)和样本生成的数字(“图 8.1.7”),这两个图像都是`z`的函数。 这两个图都说明了潜在向量如何确定所生成数字的属性:
|
||||
|
||||

|
||||
|
||||
图 8.1.6:MNIST 数字标签作为测试数据集(VAE MLP)的潜在向量平均值的函数。 原始图像可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter8-vae)中找到。
|
||||
|
||||
浏览时,连续空格始终会产生与 MNIST 数字相似的输出。 例如,数字 9 的区域接近数字 7 的区域。从中心附近的 9 移动到左下角会将数字变形为 7。从中心向上移动会将生成的数字从 3 更改为 5,最后变为 0.数字的变形在“图 8.1.7”中更明显,这是解释“图 8.1.6”的另一种方式。
|
||||
|
||||
在“图 8.1.7”中,显示生成器输出。 显示了潜在空间中数字的分布。 可以观察到所有数字都被表示。 由于中心附近分布密集,因此变化在中间迅速,在平均值较高的区域则缓慢。 我们需要记住,“图 8.1.7”是“图 8.1.6”的反映。 例如,数字 0 在两个图的左上象限中,而数字 1 在右下象限中。
|
||||
|
||||
“图 8.1.7”中存在一些无法识别的数字,尤其是在右上象限中。 从“图 8.1.6”可以看出,该区域大部分是空的,并且远离中心:
|
||||
|
||||

|
||||
|
||||
图 8.1.7:根据潜在向量平均值(VAE MLP)生成的数字。 为了便于解释,均值的范围类似于图 8.1.6
|
||||
|
||||
在本节中,我们演示了如何在 MLP 中实现 VAE。 我们还解释了导航潜在空间的结果。 在的下一部分中,我们将使用 CNN 实现相同的 VAE。
|
||||
|
||||
## 带有 CNN 的 AE
|
||||
|
||||
在原始论文《自编码变分贝叶斯》[1]中,使用 MLP 来实现 VAE 网络,这与我们在上一节中介绍的类似。 在本节中,我们将证明使用 CNN 将显着提高所产生数字的质量,并将参数数量显着减少至 134,165。
|
||||
|
||||
“列表 8.1.3”显示了编码器,解码器和 VAE 网络。 [该代码也被添加到了官方的 Keras GitHub 存储库中](https://github.com/keras-team/keras/blob/master/examples/variational_autoencoder_deconv.py)。
|
||||
|
||||
为简洁起见,不再显示与 MLP VAE 类似的某些代码行。 编码器由两层 CNN 和两层 MLP 组成,以生成潜在代码。 编码器的输出结构与上一节中看到的 MLP 实现类似。 解码器由一层`Dense`和三层转置的 CNN 组成。
|
||||
|
||||
VAE CNN 的 Keras 代码具有预训练的权重。 要测试,我们需要运行:
|
||||
|
||||
```py
|
||||
python3 vae-cnn-mnist-8.1.2.py --weights=vae_cnn_mnist.tf
|
||||
```
|
||||
|
||||
“列表 8.1.3”:`vae-cnn-mnist-8.1.2.py`
|
||||
|
||||
使用 CNN 层的`tf.keras`中的 VAE:
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
batch_size = 128
|
||||
kernel_size = 3
|
||||
filters = 16
|
||||
latent_dim = 2
|
||||
epochs = 30
|
||||
```
|
||||
|
||||
```py
|
||||
# VAE model = encoder + decoder
|
||||
# build encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
x = inputs
|
||||
for i in range(2):
|
||||
filters *= 2
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# shape info needed to build decoder model
|
||||
shape = K.int_shape(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate latent vector Q(z|X)
|
||||
x = Flatten()(x)
|
||||
x = Dense(16, activation='relu')(x)
|
||||
z_mean = Dense(latent_dim, name='z_mean')(x)
|
||||
z_log_var = Dense(latent_dim, name='z_log_var')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# use reparameterization trick to push the sampling out as input
|
||||
# note that "output_shape" isn't necessary
|
||||
# with the TensorFlow backend
|
||||
z = Lambda(sampling,
|
||||
output_shape=(latent_dim,),
|
||||
name='z')([z_mean, z_log_var])
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model(inputs, [z_mean, z_log_var, z], name='encoder')
|
||||
```
|
||||
|
||||
```py
|
||||
# build decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
|
||||
x = Dense(shape[1] * shape[2] * shape[3],
|
||||
activation='relu')(latent_inputs)
|
||||
x = Reshape((shape[1], shape[2], shape[3]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
for i in range(2):
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
filters //= 2
|
||||
```
|
||||
|
||||
```py
|
||||
outputs = Conv2DTranspose(filters=1,
|
||||
kernel_size=kernel_size,
|
||||
activation='sigmoid',
|
||||
padding='same',
|
||||
name='decoder_output')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model(latent_inputs, outputs, name='decoder')
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate VAE model
|
||||
outputs = decoder(encoder(inputs)[2])
|
||||
vae = Model(inputs, outputs, name='vae')
|
||||
```
|
||||
|
||||
“图 8.1.8”显示了 CNN 编码器模型的两个输出,即潜向量的均值和方差。 lambda 函数实现了重新参数化技巧,将随机潜码的采样推送到 VAE 网络之外:
|
||||
|
||||

|
||||
|
||||
图 8.1.8:VAE CNN 的编码器
|
||||
|
||||
“图 8.1.9”显示了 CNN 解码器模型。 2 维输入来自 lambda 函数。 输出是重构的 MNIST 数字:
|
||||
|
||||

|
||||
|
||||
图 8.1.9:VAE CNN 的解码器
|
||||
|
||||
“图 8.1.10”显示完整的 CNN VAE 模型。 通过将编码器和解码器模型结合在一起制成:
|
||||
|
||||

|
||||
|
||||
图 8.1.10:使用 CNN 的 VAE 模型
|
||||
|
||||
对 VAE 进行了 30 个周期的训练。“图 8.1.11”显示了在导航 VAE 的连续潜在空间时数字的分布。 例如,从中间到右边从 2 变为 0:
|
||||
|
||||

|
||||
|
||||
图 8.1.11:MNIST 数字标签作为测试数据集(VAE CNN)的潜在向量平均值的函数。 原始图像可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter8-vae)中找到。
|
||||
|
||||
“图 8.1.12”向我们展示了生成模型的输出。 从质量上讲,与“图 8.1.7”(具有 MLP 实现)相比,模棱两可的位数更少:
|
||||
|
||||

|
||||
|
||||
图 8.1.12:根据潜在向量平均值(VAE CNN)生成的数字。 为了便于解释,均值的范围类似于图 8.1.11
|
||||
|
||||
前的两节讨论了使用 MLP 或 CNN 的 VAE 的实现。 我们分析了两种实现方式的结果,结果表明 CNN 可以减少参数数量并提高感知质量。 在下一节中,我们将演示如何在 VAE 中实现条件,以便我们可以控制要生成的数字。
|
||||
|
||||
# 2\. 条件 VAE(CVAE)
|
||||
|
||||
有条件的 VAE [2]与 CGAN 相似。 在 MNIST 数据集的上下文中,如果随机采样潜在空间,则 VAE 无法控制将生成哪个数字。 CVAE 可以通过包含要产生的数字的条件(单标签)来解决此问题。 该条件同时施加在编码器和解码器输入上。
|
||||
|
||||
正式地,将“公式 8.1.10”中 VAE 的核心公式修改为包括条件`c`:
|
||||
|
||||
 (Equation 8.2.1)
|
||||
|
||||
与 VAE 相似,“公式 8.2.1”表示如果要最大化输出条件`c`和`P[θ](x | c)`,则必须最小化两个损失项:
|
||||
|
||||
* 给定潜在向量和条件,解码器的重建损失。
|
||||
* 给定潜在向量和条件的编码器之间的 KL 损失以及给定条件的先验分布。 与 VAE 相似,我们通常选择`P[θ](x | c) = P(x | c) = N(0, 1)`。
|
||||
|
||||
实现 CVAE 需要对 VAE 的代码进行一些修改。 对于 CVAE,使用 VAE CNN 实现是因为它可以形成一个较小的网络,并产生感知上更好的数字。
|
||||
|
||||
“列表 8.2.1”突出显示了针对 MNIST 数字的 VAE 原始代码所做的更改。 编码器输入现在是原始输入图像及其单标签的连接。 解码器输入现在是潜在空间采样与其应生成的图像的一键热标签的组合。 参数总数为 174,437。 与 β-VAE 相关的代码将在本章下一节中讨论。
|
||||
|
||||
损失函数没有改变。 但是,在训练,测试和结果绘制过程中会提供单热标签。
|
||||
|
||||
“列表 8.2.1”:`cvae-cnn-mnist-8.2.1.py`
|
||||
|
||||
`tf.keras`中使用 CNN 层的 CVAE。 重点介绍了为支持 CVAE 而进行的更改:
|
||||
|
||||
```py
|
||||
# compute the number of labels
|
||||
num_labels = len(np.unique(y_train))
|
||||
```
|
||||
|
||||
```py
|
||||
# network parameters
|
||||
input_shape = (image_size, image_size, 1)
|
||||
label_shape = (num_labels, )
|
||||
batch_size = 128
|
||||
kernel_size = 3
|
||||
filters = 16
|
||||
latent_dim = 2
|
||||
epochs = 30
|
||||
```
|
||||
|
||||
```py
|
||||
# VAE model = encoder + decoder
|
||||
# build encoder model
|
||||
inputs = Input(shape=input_shape, name='encoder_input')
|
||||
y_labels = Input(shape=label_shape, name='class_labels')
|
||||
x = Dense(image_size * image_size)(y_labels)
|
||||
x = Reshape((image_size, image_size, 1))(x)
|
||||
x = keras.layers.concatenate([inputs, x])
|
||||
for i in range(2):
|
||||
filters *= 2
|
||||
x = Conv2D(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# shape info needed to build decoder model
|
||||
shape = K.int_shape(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# generate latent vector Q(z|X)
|
||||
x = Flatten()(x)
|
||||
x = Dense(16, activation='relu')(x)
|
||||
z_mean = Dense(latent_dim, name='z_mean')(x)
|
||||
z_log_var = Dense(latent_dim, name='z_log_var')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# use reparameterization trick to push the sampling out as input
|
||||
# note that "output_shape" isn't necessary
|
||||
# with the TensorFlow backend
|
||||
z = Lambda(sampling,
|
||||
output_shape=(latent_dim,),
|
||||
name='z')([z_mean, z_log_var])
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate encoder model
|
||||
encoder = Model([inputs, y_labels],
|
||||
[z_mean, z_log_var, z],
|
||||
name='encoder')
|
||||
```
|
||||
|
||||
```py
|
||||
# build decoder model
|
||||
latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
|
||||
x = concatenate([latent_inputs, y_labels])
|
||||
x = Dense(shape[1]*shape[2]*shape[3], activation='relu')(x)
|
||||
x = Reshape((shape[1], shape[2], shape[3]))(x)
|
||||
```
|
||||
|
||||
```py
|
||||
for i in range(2):
|
||||
x = Conv2DTranspose(filters=filters,
|
||||
kernel_size=kernel_size,
|
||||
activation='relu',
|
||||
strides=2,
|
||||
padding='same')(x)
|
||||
filters //= 2
|
||||
```
|
||||
|
||||
```py
|
||||
outputs = Conv2DTranspose(filters=1,
|
||||
kernel_size=kernel_size,
|
||||
activation='sigmoid',
|
||||
padding='same',
|
||||
name='decoder_output')(x)
|
||||
```
|
||||
|
||||
```py
|
||||
# instantiate decoder model
|
||||
decoder = Model([latent_inputs, y_labels],
|
||||
outputs,
|
||||
name='decoder')
|
||||
# instantiate vae model
|
||||
outputs = decoder([encoder([inputs, y_labels])[2], y_labels])
|
||||
cvae = Model([inputs, y_labels], outputs, name='cvae')
|
||||
```
|
||||
|
||||
“图 8.2.1”显示了 CVAE 模型的编码器。 附加输入,即单热向量`class_labels`形式的条件标签表示:
|
||||
|
||||

|
||||
|
||||
图 8.2.1:CVAE CNN 中的编码器。 输入现在包括 VAE 输入和条件标签的连接
|
||||
|
||||
“图 8.2.2”显示了 CVAE 模型的解码器。 附加输入,即单热向量`class_labels`形式的条件标签表示:
|
||||
|
||||

|
||||
|
||||
图 8.2.2:CVAE CNN 中的解码器。 输入现在包括 z 采样和条件标签的连接
|
||||
|
||||
“图 8.2.3”显示了完整的 CVAE 模型,该模型是编码器和解码器结合在一起的。 附加输入,即单热向量`class_labels`形式的条件标签:
|
||||
|
||||

|
||||
|
||||
图 8.2.3:使用 CNN 的 CVAE 模型。输入现在包含一个 VAE 输入和一个条件标签
|
||||
|
||||
在“图 8.2.4”中,每个标记的平均值分布在 30 个周期后显示。 与前面章节中的“图 8.1.6”和“图 8.1.11”不同,每个标签不是集中在一个区域上,而是分布在整个图上。 这是预期的,因为潜在空间中的每个采样都应生成一个特定的数字。 浏览潜在空间会更改该特定数字的属性。 例如,如果指定的数字为 0,则在潜伏空间中导航仍将产生 0,但是诸如倾斜角度,厚度和其他书写样式方面的属性将有所不同。
|
||||
|
||||

|
||||
|
||||
图 8.2.4:作为测试数据集(CVAE CNN)的潜在向量平均值的函数的 MNIST 数字标签。 原始图像可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter8-vae)中找到。
|
||||
|
||||
“图 8.2.4”在“图 8.2.5”中更清楚地显示,数字 0 到 5。每个帧都有相同的数字,并且属性在我们浏览时顺畅地变化。 潜在代码:
|
||||
|
||||

|
||||
|
||||
图 8.2.5:根据潜在向量平均值和单热点标签(CVAE CNN)生成的数字 0 至 5。 为了便于解释,均值的范围类似于图 8.2.4。
|
||||
|
||||
“图 8.2.6”显示“图 8.2.4”,用于数字 6 至 9:
|
||||
|
||||

|
||||
|
||||
图 8.2.6:根据潜在向量平均值和单热点标签(CVAE CNN)生成的数字 6 至 9。 为了便于解释,均值的范围类似于图 8.2.4。
|
||||
|
||||
为了便于比较,潜向量的值范围与“图 8.2.4”中的相同。 使用预训练的权重,可以通过执行以下命令来生成数字(例如 0):
|
||||
|
||||
```py
|
||||
python3 cvae-cnn-mnist-8.2.1.py –bce --weights=cvae_cnn_mnist.tf --digit=0
|
||||
```
|
||||
|
||||
在“图 8.2.5”和“图 8.2.6”中,可以注意到,每个数字的宽度和圆度(如果适用)随`z[0]`的变化而变化。 从左到右追踪。 同时,当`z[1]`从上到下导航时,每个数字的倾斜角度和圆度(如果适用)也会发生变化。 随着我们离开分布中心,数字的图像开始退化。 这是可以预期的,因为潜在空间是一个圆形。
|
||||
|
||||
属性中其他明显的变化可能是数字特定的。 例如,数字 1 的水平笔划(手臂)在左上象限中可见。 数字 7 的水平笔划(纵横线)只能在右象限中看到。
|
||||
|
||||
在下一节中,我们将发现 CVAE 实际上只是另一种称为 β-VAE 的 VAE 的特例。
|
||||
|
||||
# 3\. β-VAE – 具有纠缠的潜在表示形式的 VAE
|
||||
|
||||
在“第 6 章”,“非纠缠表示 GAN”中,讨论了潜码非纠缠表示的概念和重要性。 我们可以回想起,一个纠缠的表示是单个潜伏单元对单个生成因子的变化敏感,而相对于其他因子的变化相对不变[3]。 更改潜在代码会导致生成的输出的一个属性发生更改,而其余属性保持不变。
|
||||
|
||||
在同一章中,InfoGAN [4]向我们展示了对于 MNIST 数据集,可以控制生成哪个数字以及书写样式的倾斜度和粗细。 观察上一节中的结果,可以注意到,VAE 在本质上使潜向量维解开了一定程度。 例如,查看“图 8.2.6”中的数字 8,从上到下导航`z[1]`会减小宽度和圆度,同时顺时针旋转数字。 从左至右增加`z[0]`也会在逆时针旋转数字时减小宽度和圆度。 换句话说,`z[1]`控制顺时针旋转,而`z[0]`影响逆时针旋转,并且两者都改变宽度和圆度。
|
||||
|
||||
在本节中,我们将演示对 VAE 损失函数的简单修改会迫使潜在代码进一步解开纠缠。 修改为正恒重`β > 1`,用作 KL 损失的调节器:
|
||||
|
||||
 (Equation 8.3.1)
|
||||
|
||||
VAE 的这种变化称为 β-VAE [5]。 `β`的隐含效果是更严格的标准差。 换句话说,`β`强制后验分布中的潜码`Q[φ](z | x)`独立。
|
||||
|
||||
实现 β-VAE 很简单。 例如,对于上一个示例中的 CVAE,所需的修改是`kl_loss`中的额外`beta`因子:
|
||||
|
||||
```py
|
||||
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
|
||||
kl_loss = K.sum(kl_loss, axis=-1)
|
||||
kl_loss *= -0.5 * beta
|
||||
```
|
||||
|
||||
CVAE 是 β-VAE 的特例,其中`β = 1`。 其他一切都一样。 但是,确定的值需要一些反复试验。 为了潜在的代码独立性,在重构误差和正则化之间必须有一个仔细的平衡。 解缠最大在`β = 9`附近。 当中`β = 9`的值时,β-VAE 仅被迫学习一个解纠缠的表示,而忽略另一个潜在维度。
|
||||
|
||||
“图 8.3.1”和“图 8.3.2”显示 β-VAE 的潜向量平均值,其中`β = 9`和`β = 10`:
|
||||
|
||||

|
||||
|
||||
图 8.3.1:MNIST 数字标签与测试数据集的潜在向量平均值的函数(β-VAE,`β = 9`)。 原始图像可以在该书的 [GitHub 存储库](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter8-vae)中找到。
|
||||
|
||||
`β = 9`时,与 CVAE 相比,分布具有较小的标准差。 在`β = 10`的情况下,仅学习了潜在代码。 分布实际上缩小为一个维度,编码器和解码器忽略了第一潜码`z[0]`。
|
||||
|
||||

|
||||
|
||||
图 8.3.2:MNIST 数字标签与测试数据集的潜向量平均值的函数(β-VAE 和`β = 10`)
|
||||
|
||||
[原始图像可以在该书的 GitHub 存储库中找到](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter8-vae)。
|
||||
|
||||
这些观察结果反映在“图 8.3.3”中。 具有`β = 9`的 β-VAE 具有两个实际上独立的潜在代码。 `z[0]`确定书写样式的倾斜度,而`z[1]`指定数字的宽度和圆度(如果适用)。 对于中`β = 10`的 β-VAE,`z[0]`被静音。 `z[0]`的增加不会显着改变数字。`z[1]`确定书写样式的倾斜角度和宽度:
|
||||
|
||||

|
||||
|
||||
图 8.3.3:根据潜在向量平均值和单热点标签(β-VAE,`β = 1, 9, 10`)生成的数字 0 至 3。 为了便于解释,均值的范围类似于图 8.3.1。
|
||||
|
||||
β-VAE 的`tf.keras`代码具有预训练的权重。 要使用`β = 9`生成数字 0 来测试 β-VAE,我们需要运行以下命令:
|
||||
|
||||
```py
|
||||
python3 cvae-cnn-mnist-8.2.1.py --beta=9 --bce --weights=beta-cvae_cnn_mnist.tf --digit=0
|
||||
```
|
||||
|
||||
总而言之,我们已经证明与 GAN 相比,在 β-VAE 上更容易实现解缠表示学习。 我们所需要做的就是调整单个超参数。
|
||||
|
||||
# 4\. 总结
|
||||
|
||||
在本章中,我们介绍了 VAE 的原理。 正如我们从 VAE 原理中学到的那样,从两次尝试从潜在空间创建合成输出的角度来看,它们都与 GAN 相似。 但是,可以注意到,与 GAN 相比,VAE 网络更简单,更容易训练。 越来越清楚的是 CVAE 和 β-VAE 在概念上分别类似于条件 GAN 和解缠表示 GAN。
|
||||
|
||||
VAE 具有消除潜在向量纠缠的内在机制。 因此,构建 β-VAE 很简单。 但是,我们应该注意,可解释和解开的代码对于构建智能体很重要。
|
||||
|
||||
在下一章中,我们将专注于强化学习。 在没有任何先验数据的情况下,智能体通过与周围的世界进行交互来学习。 我们将讨论如何为智能体的正确行为提供奖励,并为错误的行为提供惩罚。
|
||||
|
||||
# 5\. 参考
|
||||
|
||||
1. `Diederik P. Kingma and Max Welling. Auto-encoding Variational Bayes. arXiv preprint arXiv:1312.6114, 2013 (https://arxiv.org/pdf/1312.6114.pdf).`
|
||||
1. `Kihyuk Sohn, Honglak Lee, and Xinchen Yan. Learning Structured Output Representation Using Deep Conditional Generative Models. Advances in Neural Information Processing Systems, 2015 (http://papers.nips.cc/paper/5775-learning-structured-output-representation-using-deep-conditional-generative-models.pdf).`
|
||||
1. `Yoshua Bengio, Aaron Courville, and Pascal Vincent. Representation Learning.`
|
||||
1. `A Review and New Perspectives. IEEE transactions on Pattern Analysis and Machine Intelligence 35.8, 2013: 1798-1828 (https://arxiv.org/pdf/1206.5538.pdf).`
|
||||
1. `Xi Chen et al.: Infogan: Interpretable Representation Learning by Information Maximizing Generative Adversarial Nets. Advances in Neural Information Processing Systems, 2016 (http://papers.nips.cc/paper/6399-infogan-interpretable-representation-learning-by-information-maximizing-generative-adversarial-nets.pdf).`
|
||||
1. `I. Higgins, L. Matthey, A. Pal, C. Burgess, X. Glorot, M. Botvinick, S. Mohamed, and A. Lerchner. -VAE: Learning Basic Visual Concepts with a Constrained Variational Framework. ICLR, 2017 (https://openreview.net/pdf?id=Sy2fzU9gl).`
|
||||
1. `Carl Doersch. Tutorial on variational autoencoders. arXiv preprint arXiv:1606.05908, 2016 (https://arxiv.org/pdf/1606.05908.pdf).`
|
||||
@@ -1,420 +0,0 @@
|
||||
# 十二、语义分割
|
||||
|
||||
在“第 11 章”,“对象检测”中,我们讨论了对象检测作为一种重要的计算机视觉算法,具有多种实际应用。 在本章中,我们将讨论另一种称为语义分割的相关算法。 如果对象检测的目的是对图像中的每个对象同时执行定位和标识,则在语义分割中,目的是根据每个像素的对象类别对它们进行分类。
|
||||
|
||||
进一步扩展类比,在对象检测中,我们使用边界框显示结果。 在语义分割中,同一对象的所有像素都属于同一类别。 在视觉上,同一对象的所有像素将具有相同的颜色。 例如,属于*汽水**类别的所有像素均为蓝色。 非苏打罐对象的像素将具有不同的颜色。
|
||||
|
||||
类似于对象检测,语义分割有许多实际应用。 在医学成像中,它可用于分离和测量正常细胞与异常细胞的区域。 在卫星成像中,语义分段可用于度量森林覆盖率或灾难期间的洪水程度。 通常,语义分割可用于识别属于同一类对象的像素。 识别每个对象的各个实例并不重要。
|
||||
|
||||
好奇的读者可能会想知道,一般而言,不同的分割算法与特别是语义分割算法之间有什么区别? 在以下部分中,我们将对不同的分割算法进行限定。
|
||||
|
||||
总而言之,本章的目的是为了提出:
|
||||
|
||||
* 不同类型的分割算法
|
||||
* **全卷积网络**(**FCN**)作为语义分割算法的实现
|
||||
* `tf.keras`中 FCN 的实现和评估
|
||||
|
||||
我们将从讨论不同的分割算法开始。
|
||||
|
||||
# 1\. 分割
|
||||
|
||||
分割算法将图像划分为像素或区域集。 分区的目的是为了更好地理解图像表示的内容。 像素组可以表示图像中特定应用感兴趣的对象。 我们划分的方式区分了不同的分割算法。
|
||||
|
||||
在某些应用中,我们对给定图像中的特定可数对象感兴趣。 例如,在自主导航中,我们对车辆,交通标志,行人和道路上的其他物体的实例感兴趣。 这些可计数对象统称为,称为**事物**。 所有其他像素都集中在一起作为背景。 这种类型的细分称为**实例细分**。
|
||||
|
||||
在其他应用中,我们对可数对象不感兴趣,而对无定形的不可数区域感兴趣,例如天空,森林,植被,道路,草地,建筑物和水体。 这些对象统称为东西。 这种类型的分段称为**语义分段**。
|
||||
|
||||
大致上,**事物**和**事物**共同构成了整个图像。 如果算法可以识别事物像素和填充像素,则其称为**全光分割**,如 Kirilov 等人所定义 [1]。
|
||||
|
||||
但是,事物与事物之间的区别并不严格。 应用可能将可数对象统称为东西。 例如,在百货商店中,不可能识别机架上的服装实例。 它们可以作为布料一起集中在一起。
|
||||
|
||||
“图 12.1.1”显示了不同类型的细分之间的区别。 输入的图像在桌子的顶部显示了两个汽水罐和两个果汁罐。 背景杂乱无章。 假设我们只对汽水罐和果汁罐感兴趣,在实例细分中,我们为每个对象实例分配唯一的颜色以分别区分四个对象。 对于语义分割,我们假设将所有的汽水罐都塞在一起,将果汁罐作为另一罐塞在一起,将背景作为最后的罐塞在一起。 基本上,我们为每种物料分配了唯一的颜色。 最后,在全景分割中,我们假设只有背景才是背景,而我们只对苏打水和果汁罐感兴趣。
|
||||
|
||||
对于这本书,我们仅探讨语义分割。 按照“图 12.1.1”中的示例,我们将为“第 11 章”,“对象检测”中使用的对象分配唯一的填充类别:1)水瓶,2)**汽水罐**和 3)**果汁罐**。 第四个也是最后一个类别是背景。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
图 12.1.1:显示不同分割算法的四幅图像。 彩色效果最佳。 原始图像可以在[这个页面](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter12-segmentation)中找到。
|
||||
|
||||
# 2\. 语义分割网络
|
||||
|
||||
从上一节中,我们了解到语义分割网络是一个像素级分类器。 网络框图显示在“图 12.2.1”中。 但是,与简单分类器不同(例如,“第 1 章”,“Keras 深度神经网络”和“第 2 章”,“MNIST 分类器简介”) 其中只有一个分类器生成`one-hot vector`作为输出,在语义分段中,我们有并行运行的并行分类器。 每个人都在生成自己的单热点向量预测。 分类器的数量等于输入图像中的像素数量或图像宽度与高度的乘积。 每个`one-hot vector`预测的维数等于感兴趣的填充对象类别的数量。
|
||||
|
||||

|
||||
|
||||
图 12.2.1:可以将语义分割网络视为按像素分类器。 彩色效果最佳。 原始图像可以在[这个页面](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter12-segmentation)中找到
|
||||
|
||||
例如,假设我们对以下四个类别感兴趣:0)**背景**,1)**水瓶**,2)**汽水罐**和 3)**果汁罐**,我们可以在“图 12.2.2”中看到,每个对象类别有四个像素。
|
||||
|
||||
相应地,使用 4 维`one-hot vector`对每个像素进行分类。 我们使用阴影表示像素的类别。 利用这一知识,我们可以想象一个语义分割网络预测`image_width x image_height` 4 维一热向量作为输出,每个像素一个 4 维一热向量:
|
||||
|
||||

|
||||
|
||||
图 12.2.2:四个不同的样本像素。 使用 4 维一热向量,每个像素根据其类别进行分类。 彩色效果最佳。 原始图像可以在[这个页面](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter12-segmentation)中找到
|
||||
|
||||
了解了语义分割的概念后,我们现在可以介绍神经网络像素级分类器。 Long 等人的《全卷积网络(FCN)》启发了我们的语义分段网络架构 [2]。FCN 的关键思想是在生成最终预测时使用多个比例的特征映射。
|
||||
|
||||
我们的语义分段网络显示在“图 12.2.3”中。 它的输入是 RGB 图像(例如`640 x 480 x 3`),并且输出具有类似尺寸的张量,但最后一个尺寸是填充类别的数量(例如,对于 4 种填充类别而言是`640 x 480 x 4`)。 出于可视化目的,我们通过为每种类别分配颜色来将输出映射到 RGB:
|
||||
|
||||

|
||||
|
||||
图 12.2.3:语义分割的网络架构。 除非另有说明,否则核大小为 3。 除非另有说明,否则跨步为 1。 彩色效果最佳。 原始图像可以在[这个页面](https://github.com/PacktPublishing/Advanced-Deep-Learning-with-Keras/tree/master/chapter12-segmentation)中找到
|
||||
|
||||
类似于“第 11 章”,“对象检测”中讨论的 SSD,我们采用骨干网作为特征提取器。 我们在 SSD 中使用类似的 ResNetv2 网络。 ResNet 主干网执行两次最大池化,以到达第一组特征映射,其尺寸为输入图像的 1/4。 通过使用连续的`Conv2D(strides=2)-BN-ReLU`层生成其他特征映射集,从而生成具有输入图像尺寸`(1/8, 1/16, 1/32)`的特征映射。
|
||||
|
||||
Zhao 等人的《金字塔场景解析网络(PSPNet)》进行了改进,进一步增强了我们的语义分割网络架构 [3]。 在 PSPNet 中,每个特征映射由另一个卷积层进一步处理。 此外,还使用了第一组特征映射。
|
||||
|
||||
FCN 和 PSPNet 都对特征金字塔进行了上采样,以达到与第一组特征映射相同的大小。 之后,使用`Concatenate`层将所有上采样特征融合在一起。 然后级联层通过步长等于 2 的转置卷积处理两次,以恢复原始图像的宽度和高度。 最后,使用核大小为 1 且过滤器等于 4(换句话说,类别数)和`Softmax`层的转置卷积生成按像素分类预测。
|
||||
|
||||
在下一节中,我们将讨论细分网络的`tf.keras`实现。 我们可以重用“第 11 章”,“对象检测”中的 SSD 中的某些网络块,以加快实现速度。
|
||||
|
||||
# 3\. Keras 中的语义分割网络
|
||||
|
||||
如图“图 12.2.3”所示,我们已经有了语义细分网络的一些关键构建块。 我们可以重用“第 2 章”,“深度神经网络”中介绍的 ResNet 模型。 我们只需要构建特征的金字塔以及上采样和预测层。
|
||||
|
||||
借用我们在“第 2 章”,“深度神经网络”中开发的 ResNet 模型,并在“第 11 章”,“对象检测”中重用了该模型, 我们提取具有四个级别的特征金字塔。“列表 12.3.1”显示了从 ResNet 提取特征的金字塔。 `conv_layer()`只是创建`Conv2D(strides=2)-BN-ReLU`层的辅助函数。
|
||||
|
||||
“列表 12.3.1”:`resnet.py`:
|
||||
|
||||
特征的金字塔函数:
|
||||
|
||||
```py
|
||||
def features_pyramid(x, n_layers):
|
||||
"""Generate features pyramid from the output of the
|
||||
last layer of a backbone network (e.g. ResNetv1 or v2)
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
x (tensor): Output feature maps of a backbone network
|
||||
n_layers (int): Number of additional pyramid layers
|
||||
```
|
||||
|
||||
```py
|
||||
Return:
|
||||
outputs (list): Features pyramid
|
||||
"""
|
||||
outputs = [x]
|
||||
conv = AveragePooling2D(pool_size=2, name='pool1')(x)
|
||||
outputs.append(conv)
|
||||
prev_conv = conv
|
||||
n_filters = 512
|
||||
```
|
||||
|
||||
```py
|
||||
# additional feature map layers
|
||||
for i in range(n_layers - 1):
|
||||
postfix = "_layer" + str(i+2)
|
||||
conv = conv_layer(prev_conv,
|
||||
n_filters,
|
||||
kernel_size=3,
|
||||
strides=2,
|
||||
use_maxpool=False,
|
||||
postfix=postfix)
|
||||
outputs.append(conv)
|
||||
prev_conv = conv
|
||||
```
|
||||
|
||||
```py
|
||||
return outputs
|
||||
```
|
||||
|
||||
“列表 12.3.1”只是特征金字塔的一半。 剩下的一半是每组特征之后的卷积。 另一半显示在“列表 12.3.2”中,以及金字塔各层的上采样。 例如,图像尺寸为 1/8 的特征会被上采样 2 倍,以使其尺寸与图像尺寸为 1/4 的第一组特征相匹配。 在同一清单中,我们还建立了完整的分割模型,从骨干网络到特征金字塔,再连接上采样特征金字塔,最后进一步进行特征提取,上采样和预测。 我们在输出层使用`n`维(例如 4 维)`Softmax`层执行逐像素分类。
|
||||
|
||||
“列表 12.3.2”:`model.py`:
|
||||
|
||||
构建语义分割网络:
|
||||
|
||||
```py
|
||||
def build_fcn(input_shape,
|
||||
backbone,
|
||||
n_classes=4):
|
||||
"""Helper function to build an FCN model.
|
||||
|
||||
Arguments:
|
||||
backbone (Model): A backbone network
|
||||
such as ResNetv2 or v1
|
||||
n_classes (int): Number of object classes
|
||||
including background.
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
inputs = Input(shape=input_shape)
|
||||
features = backbone(inputs)
|
||||
```
|
||||
|
||||
```py
|
||||
main_feature = features[0]
|
||||
features = features[1:]
|
||||
out_features = [main_feature]
|
||||
feature_size = 8
|
||||
size = 2
|
||||
# other half of the features pyramid
|
||||
# including upsampling to restore the
|
||||
# feature maps to the dimensions
|
||||
# equal to 1/4 the image size
|
||||
for feature in features:
|
||||
postfix = "fcn_" + str(feature_size)
|
||||
feature = conv_layer(feature,
|
||||
filters=256,
|
||||
use_maxpool=False,
|
||||
postfix=postfix)
|
||||
postfix = postfix + "_up2d"
|
||||
feature = UpSampling2D(size=size,
|
||||
interpolation='bilinear',
|
||||
name=postfix)(feature)
|
||||
size = size * 2
|
||||
feature_size = feature_size * 2
|
||||
out_features.append(feature)
|
||||
```
|
||||
|
||||
```py
|
||||
# concatenate all upsampled features
|
||||
x = Concatenate()(out_features)
|
||||
# perform 2 additional feature extraction
|
||||
# and upsampling
|
||||
x = tconv_layer(x, 256, postfix="up_x2")
|
||||
x = tconv_layer(x, 256, postfix="up_x4")
|
||||
# generate the pixel-wise classifier
|
||||
x = Conv2DTranspose(filters=n_classes,
|
||||
kernel_size=1,
|
||||
strides=1,
|
||||
padding='same',
|
||||
kernel_initializer='he_normal',
|
||||
name="pre_activation")(x)
|
||||
x = Softmax(name="segmentation")(x)
|
||||
```
|
||||
|
||||
```py
|
||||
model = Model(inputs, x, name="fcn")
|
||||
```
|
||||
|
||||
```py
|
||||
return model
|
||||
```
|
||||
|
||||
给定分割网络模型,我们使用学习速度为`1e-3`的 Adam 优化器和分类交叉熵损失函数来训练网络。“列表 12.3.3”显示了模型构建和训练函数调用。 在 40 个周期之后,学习率每 20 个周期减半。 我们使用`AccuracyCallback`监视网络表现,类似于“第 11 章”,“对象检测”中的 SSD 网络。 回调使用类似于对象检测平均 IoU 的**平均 IoU**(**mIoU**)指标计算表现。 表现最佳的平均值 IoU 的权重保存在文件中。 通过调用`fit_generator()`将网络训练 100 个周期。
|
||||
|
||||
“列表 12.3.3”:`fcn-12.3.1.py`:
|
||||
|
||||
语义分割网络的初始化和训练:
|
||||
|
||||
```py
|
||||
def build_model(self):
|
||||
"""Build a backbone network and use it to
|
||||
create a semantic segmentation
|
||||
network based on FCN.
|
||||
"""
|
||||
```
|
||||
|
||||
```py
|
||||
# input shape is (480, 640, 3) by default
|
||||
self.input_shape = (self.args.height,
|
||||
self.args.width,
|
||||
self.args.channels)
|
||||
```
|
||||
|
||||
```py
|
||||
# build the backbone network (eg ResNet50)
|
||||
# the backbone is used for 1st set of features
|
||||
# of the features pyramid
|
||||
self.backbone = self.args.backbone(self.input_shape,
|
||||
n_layers=self.args.layers)
|
||||
```
|
||||
|
||||
```py
|
||||
# using the backbone, build fcn network
|
||||
# output layer is a pixel-wise classifier
|
||||
self.n_classes = self.train_generator.n_classes
|
||||
self.fcn = build_fcn(self.input_shape,
|
||||
self.backbone,
|
||||
self.n_classes)
|
||||
```
|
||||
|
||||
```py
|
||||
def train(self):
|
||||
"""Train an FCN"""
|
||||
optimizer = Adam(lr=1e-3)
|
||||
loss = 'categorical_crossentropy'
|
||||
self.fcn.compile(optimizer=optimizer, loss=loss)
|
||||
```
|
||||
|
||||
```py
|
||||
log = "# of classes %d" % self.n_classes
|
||||
print_log(log, self.args.verbose)
|
||||
log = "Batch size: %d" % self.args.batch_size
|
||||
print_log(log, self.args.verbose)
|
||||
```
|
||||
|
||||
```py
|
||||
# prepare callbacks for saving model weights
|
||||
# and learning rate scheduler
|
||||
# model weights are saved when test iou is highest
|
||||
# learning rate decreases by 50% every 20 epochs
|
||||
# after 40th epoch
|
||||
accuracy = AccuracyCallback(self)
|
||||
scheduler = LearningRateScheduler(lr_scheduler)
|
||||
```
|
||||
|
||||
```py
|
||||
callbacks = [accuracy, scheduler]
|
||||
# train the fcn network
|
||||
self.fcn.fit_generator(generator=self.train_generator,
|
||||
use_multiprocessing=True,
|
||||
callbacks=callbacks,
|
||||
epochs=self.args.epochs,
|
||||
workers=self.args.workers)
|
||||
```
|
||||
|
||||
多线程数据生成器类`DataGenerator`与“第 11 章”,“对象检测”中使用的类类似。 如“列表 12.3.4”所示,对`__data_generation(self, keys)`签名方法进行了修改,以生成一对图像张量及其相应的按像素方向的真实情况标签或分割蒙版 。 在下一节中,我们将讨论如何生成基本事实标签。
|
||||
|
||||
“列表 12.3.4”:`data_generator.py`:
|
||||
|
||||
`DataGenerator`类用于语义分割的数据生成方法:
|
||||
|
||||
```py
|
||||
def __data_generation(self, keys):
|
||||
"""Generate train data: images and
|
||||
segmentation ground truth labels
|
||||
```
|
||||
|
||||
```py
|
||||
Arguments:
|
||||
keys (array): Randomly sampled keys
|
||||
(key is image filename)
|
||||
```
|
||||
|
||||
```py
|
||||
Returns:
|
||||
x (tensor): Batch of images
|
||||
y (tensor): Batch of pixel-wise categories
|
||||
"""
|
||||
# a batch of images
|
||||
x = []
|
||||
# and their corresponding segmentation masks
|
||||
y = []
|
||||
```
|
||||
|
||||
```py
|
||||
for i, key in enumerate(keys):
|
||||
# images are assumed to be stored
|
||||
# in self.args.data_path
|
||||
# key is the image filename
|
||||
image_path = os.path.join(self.args.data_path, key)
|
||||
image = skimage.img_as_float(imread(image_path))
|
||||
# append image to the list
|
||||
x.append(image)
|
||||
# and its corresponding label (segmentation mask)
|
||||
labels = self.dictionary[key]
|
||||
y.append(labels)
|
||||
```
|
||||
|
||||
```py
|
||||
return np.array(x), np.array(y)
|
||||
```
|
||||
|
||||
语义分割网络现已完成。 使用`tf.keras`,我们讨论了其架构实现,初始化和训练。
|
||||
|
||||
在运行训练程序之前,我们需要训练和测试带有地面真实性标签的数据集。 在的下一部分中,我们将讨论将在本章中使用的语义分割数据集。
|
||||
|
||||
# 4\. 示例数据集
|
||||
|
||||
我们可以使用在“第 11 章”,“对象检测”中使用的数据集。 回想一下,我们使用了一个小型数据集,其中包含使用便宜的 USB 相机(A4TECH PK-635G)收集的 1,000 `640 x 480` RGB 训练图像和 50 `640 x 480` RGB 测试图像。 但是,我们没有使用边界框和类别进行标记,而是使用多边形形状跟踪了每个对象类别的边缘。 我们使用相同的数据集标注器 **VGG 图像标注器**(**VIA**)[4]手动跟踪边缘并分配以下标签:1)**水瓶**,2)**汽水罐**和 3)**果汁罐**。
|
||||
|
||||
“图 12.4.1”显示了标记过程的示例 UI。
|
||||
|
||||

|
||||
|
||||
图 12.4.1:使用 VGG 图像标注器(VIA)进行语义分割的数据集标记过程
|
||||
|
||||
威盛标签软件将标签保存在 JSON 文件中。 对于训练和测试数据集,这些是:
|
||||
|
||||
```py
|
||||
segmentation_train.json
|
||||
segmentation_test.json
|
||||
```
|
||||
|
||||
无法原样使用存储在 JSON 文件中的多边形区域。 每个区域都必须转换成分割蒙版,即张量,其尺寸为`img_w x img_h x px – wise_category`。 在此数据集中,分割蒙版的尺寸为`640 x 480 x 4`。类别 0 为背景,其余为 1)对于**水瓶**,2)对于**苏打罐**,以及 3)表示**果汁罐**。 在`utils`文件夹中,我们创建了一个`generate_gt_segmentation.py`工具,用于将 JSON 文件转换为分段掩码。 为了方便起见,用于训练和测试的地面真实数据存储在压缩数据集中,该数据集是从[上一章](https://bit.ly/adl2-ssd)下载的:
|
||||
|
||||
```py
|
||||
segmentation_train.npy
|
||||
segmentation_test.npy
|
||||
```
|
||||
|
||||
每个文件都包含`image filename: segmentation mask`格式的真实情况数据字典,该字典在训练和验证期间加载。“图 12.4.2”显示了使用彩色像素可视化的“图 12.4.1”中图像的分割蒙版的示例。
|
||||
|
||||

|
||||
|
||||
图 12.4.2:可视化图 12.4.1 中所做标注的分段蒙版
|
||||
|
||||
现在,我们准备训练和验证语义分割网络。 在下一节中,我们将显示在本节中标注的数据集上语义分割的结果。
|
||||
|
||||
# 5\. 语义分割验证
|
||||
|
||||
要训练语义分段网络,请运行以下命令:
|
||||
|
||||
```py
|
||||
python3 fcn-12.3.1.py --train
|
||||
```
|
||||
|
||||
在每个周期,也会执行验证以确定表现最佳的参数。 对于语义分割,可以使用两个度量。 首先是平均 IOU。 这类似于上一章中目标检测中的平均 IoU。 区别在于针对每个填充类别在真实情况分割掩码和预测的分割掩码之间计算 IoU。 这包括背景。 平均 IoU 只是测试数据集所有 IoU 的平均值。
|
||||
|
||||
“图 12.5.1”显示了在每个周期使用 mIoU 的语义分割网络的表现。 最大 mIoU 为 0.91。 这个比较高。 但是,我们的数据集只有四个对象类别:
|
||||
|
||||

|
||||
|
||||
图 12.5.1:使用 mIoU 进行测试数据集训练期间的语义分割表现
|
||||
|
||||
第二个指标是平均像素精度。 这类似于在分类器预测上计算准确率的方式。 不同之处在于,分割网络具有的预测数量等于图像中的像素数量,而不是具有一个预测。 对于每个测试输入图像,计算平均像素精度。 然后,计算所有测试图像的平均值。
|
||||
|
||||
“图 12.5.2”显示了在每个周期使用平均像素精度的语义分割网络的表现。 最大平均像素精度为 97.9%。 我们可以看到平均像素精度与 mIoU 之间的相关性:
|
||||
|
||||

|
||||
|
||||
图 12.5.2:使用测试数据集的平均像素精度在训练期间的语义分割表现
|
||||
|
||||
“图 12.5.3”显示了输入图像,地面实况语义分割掩码和预测的语义分割掩码的样本:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
图 12.5.3:样本输入,基本事实和语义细分的预测。 我们将黑色分配为背景类,而不是紫色,如先前所用
|
||||
|
||||
总体而言,我们基于 FCN 并经过 PSPNet 的思想改进的语义分割网络的表现相对较好。 我们的语义分割网络绝不是最优化的。 可以减少特征金字塔中的过滤器数量,以最大程度地减少参数的数量,该参数约为 1110 万。 探索增加特征金字塔中的级别数也很有趣。 读者可以通过执行以下命令来运行验证:
|
||||
|
||||
```py
|
||||
python3 fcn-12.3.1.py --evaluate
|
||||
--restore-weights=ResNet56v2-3layer-drinks-best-iou.h5
|
||||
```
|
||||
|
||||
在下一章中,我们将介绍无监督的学习算法。 考虑到监督学习中所需的昂贵且费时的标签,强烈地开发了无监督学习技术。 例如,在本章的语义分割数据集中,一个人花了大约 4 天的手工标签。 如果深度学习始终需要人工标记,那么它就不会前进。
|
||||
|
||||
# 6\. 总结
|
||||
|
||||
在本章中,讨论了分割的概念。 我们了解到细分有不同类别。 每个都有自己的目标应用。 本章重点介绍语义分段的网络设计,实现和验证。
|
||||
|
||||
我们的语义分割网络受到 FCN 的启发,FCN 已成为许多现代,最先进的分割算法(例如 Mask-R-CNN [5])的基础。 PSPNet 的构想进一步增强了我们的网络,该构想在 ImageNet 2016 解析挑战赛中获得第一名。
|
||||
|
||||
使用 VIA 标记工具,使用与“第 11 章”,“对象检测”中使用的相同图像集生成用于语义分割的新数据集标签。 分割蒙版标记属于同一对象类的所有像素。
|
||||
|
||||
我们使用平均 IoU 和平均像素准确率指标对语义分割网络进行了训练和验证。 测试数据集上的表现表明,它可以有效地对测试图像中的像素进行分类。
|
||||
|
||||
如本章最后一部分所述,由于所涉及的成本和时间,深度学习领域正在意识到监督学习的局限性。 下一章重点介绍无监督学习。 它利用了通信领域信息理论中使用的互信息概念。
|
||||
|
||||
# 7\. 参考
|
||||
|
||||
1. `Kirillov, Alexander, et al.: Panoptic Segmentation. Proceedings of the IEEE conference on computer vision and pattern recognition. 2019.`
|
||||
1. `Long, Jonathan, Evan Shelhamer, and Trevor Darrell: Fully Convolutional Networks for Semantic Segmentation. Proceedings of the IEEE conference on computer vision and pattern recognition. 2015.`
|
||||
1. `Zhao, Hengshuang, et al.: Pyramid Scene Parsing Network. Proceedings of the IEEE conference on computer vision and pattern recognition. 2017.`
|
||||
1. `Dutta, et al.: VGG Image Annotator http://www.robots.ox.ac.uk/~vgg/software/via/`
|
||||
1. `He Kaiming, et al.: Mask R-CNN. Proceedings of the IEEE international conference on computer vision. 2017.`
|
||||
@@ -1,35 +0,0 @@
|
||||
# TensorFlow 2 和 Keras 高级深度学习
|
||||
|
||||
> 原文:[Advanced Deep Learning with TensorFlow 2 and Keras](https://b-ok.global/book/5559514/a60246)
|
||||
>
|
||||
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
>
|
||||
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
|
||||
>
|
||||
> 不要担心自己的形象,只关心如何实现目标。——《原则》,生活原则 2.3.c
|
||||
|
||||
* [在线阅读](https://dl.apachecn.org)
|
||||
* [ApacheCN 面试求职交流群 724187166](https://jq.qq.com/?_wv=1027&k=54ujcL3)
|
||||
* [ApacheCN 学习资源](http://www.apachecn.org/)
|
||||
|
||||
## 贡献指南
|
||||
|
||||
本项目需要校对,欢迎大家提交 Pull Request。
|
||||
|
||||
> 请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)
|
||||
|
||||
## 联系方式
|
||||
|
||||
### 负责人
|
||||
|
||||
* [飞龙](https://github.com/wizardforcel): 562826179
|
||||
|
||||
### 其他
|
||||
|
||||
* 在我们的 [apachecn/apachecn-tf-zh](https://github.com/apachecn/apachecn-tf-zh) github 上提 issue.
|
||||
* 发邮件到 Email: `apachecn@163.com`.
|
||||
* 在我们的 [组织学习交流群](http://www.apachecn.org/organization/348.html) 中联系群主/管理员即可.
|
||||
|
||||
## 赞助我们
|
||||
|
||||

|
||||
@@ -1,15 +0,0 @@
|
||||
+ [TensorFlow 2 和 Keras 高级深度学习](README.md)
|
||||
+ [零、前言](00.md)
|
||||
+ [一、使用 Keras 入门高级深度学习](01.md)
|
||||
+ [二、深度神经网络](02.md)
|
||||
+ [三、自编码器](03.md)
|
||||
+ [四、生成对抗网络(GAN)](04.md)
|
||||
+ [五、改进的 GAN](05.md)
|
||||
+ [六、纠缠表示 GAN](06.md)
|
||||
+ [七、跨域 GAN](07.md)
|
||||
+ [八、变分自编码器(VAE)](08.md)
|
||||
+ [九、深度强化学习](09.md)
|
||||
+ [十、策略梯度方法](10.md)
|
||||
+ [十一、对象检测](11.md)
|
||||
+ [十二、语义分割](12.md)
|
||||
+ [十三、使用互信息的无监督学习](13.md)
|
||||
|
Before Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 461 B |
|
Before Width: | Height: | Size: 241 B |
|
Before Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 205 B |
|
Before Width: | Height: | Size: 492 B |
|
Before Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 214 B |
|
Before Width: | Height: | Size: 635 B |
|
Before Width: | Height: | Size: 794 B |
|
Before Width: | Height: | Size: 402 B |
|
Before Width: | Height: | Size: 365 B |
|
Before Width: | Height: | Size: 278 B |
|
Before Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 195 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 794 B |
|
Before Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 233 B |
|
Before Width: | Height: | Size: 242 B |
|
Before Width: | Height: | Size: 1017 B |
|
Before Width: | Height: | Size: 203 B |
|
Before Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 201 B |
|
Before Width: | Height: | Size: 636 B |
|
Before Width: | Height: | Size: 342 B |
|
Before Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 784 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 407 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 235 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 186 B |
|
Before Width: | Height: | Size: 190 B |
|
Before Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 332 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 688 B |
|
Before Width: | Height: | Size: 678 B |
|
Before Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 202 B |
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 236 B |
|
Before Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 408 B |
|
Before Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 189 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 213 B |