deeplearning

资源目录

资源列表

学习计划

TensorFlow Env

1.TensorFlow 支持的硬件平台

  • PC

    • CPU

    • GPU

      • 一块主流的 NVIDIA GPU 会大幅提高训练速度

      • 显卡的 CUDA 核心数和显存大小是决定机器学习性能的两个关键参数

        • 显卡的 CUDA 核心数决定训练速度
        • 显卡的 显存大小 是决定能够训练多大的模型以及训练时的最大批次大小(batch size)
      • 对于前沿的机器学习研究,尤其是计算机视觉和自然语言处理领域而言,对 GPU 并行训练是标准配置

    • Cloud TPU(张量)

  • Mobile

    • Android
    • iOS
    • Embedded Devices

2.TensorFlow 系统要求

  • Python 3.5-3.7
  • pip >= 19.0(需要 manylinux2010 支持)
  • Ubuntu 16.04 or later(64位)
  • Windows 7 or later(64位, 仅支持 Python 3)
  • macOS 10.12.6(Sierra) or later(64位, no GPU support)
  • Raspbian 9.0 or later
  • GPU 支持需要使用支持 CUDA® 的显卡(适用于 Ubuntu 和 Windows)

3.安装 TensorFlow 2

Important

TensorFlow pip packages

  • tensorflow: 支持 CPU 和 GPU 的最新稳定版(适用于 Ubuntu 和 Windows)
  • tf-nightly: 预览 build(不稳定)。Ubuntu 和 Windows 均包含 GPU 支持
  • tensorflow-gpu: Current release with GPU support (Ubuntu and Windows)
  • tf-nightly-gpu: Nightly build with GPU support (unstable, Ubuntu and Windows)

3.1 使用 pip 安装 TensorFlow 2

Important

  • 必须使用最新版本的 pip, 才能安装 TensorFlow 2
  • Virtualenv 安装

    # Requires the latest pip
    (venv) $ pip install --upgrade pip
    
    # Current stable release for CPU and GPU
    (venv) $ pip install --upgrade tensorflow
    (venv) $ python -c "import tensorflow as tf;print(tf.reduce_sum(tf.random.normal([1000, 1000])))"
    
  • 系统安装

    # Requires the latest pip
    $ pip3 install --upgrade pip
    
    # Current stable release for CPU and GPU
    $ pip3 install --user --upgrade tensorflow
    $ python3 -c "import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000, 1000])))"
    
    $ pip3 install pydot
    

3.2 使用 conda 安装 TensorFlow 2

1.安装 Python 环境

建议安装 Anaconda 的 Python 3.7 64 位版本

2.使用 Anaconda/Miniconda 自带的 conda 包管理器建立一个 conda 虚拟环境,并进入虚拟环境

$ conda create --name tf2 python=3.7
conda activate tf2

3.使用 Python 包管理器 pip 安装 TensorFlow

pip install tensorflow

3.3 使用 Docker 安装 TensorFlow 2

  • TensorFlow Docker 映像已经过配置,可运行 TensorFlow。Docker 容器可在虚拟环境中运行,是设置 GPU 支持的最简单方法。

    # Download latest stable image
    $ docker pull tensorflow/tensorflow:latest-py3
    
    # Start Jupyter server
    $ docker run -it -p 8888:8888 tensorflow/tensorflow:latest-py3-jupyter
    

Note

小技巧

  • 也可以使用 conda install tensorflow 或者 conda install tensorflow-gpu 命令安装 TensorFlow, 不过 conda 源的版本往往更新较慢,难以在第一时间获得最新的 TensorFlow 版本
  • 从 TensorFlow 2.1 开始,pip 包 tensorflow 同时包含 GPU 支持,无须通过特定的 pip 包 tensorflow-gpu 安装 GPU 版本。 如果对 pip 包的大小敏感,可使用 tensorflow-cpu 包安装仅支持 CPU 的 Tensorflow 版本
  • 在 Windows 系统下,需要打开 “开始” 菜单中的 “Anaconda Prompt” 进入 Anaonda 的命令行环境

3.4 GPU 版本的 TensorFlow 安装

GPU 版本的 TensorFlow 可以利用 NVIDIA GPU 强大的加速计算能力,使 TensorFlow 运行更加高效,尤其是可以成倍提升模型的训练速度.

在安装 GPU 版本的 TensorFlow 前,需要有一块“不太旧”的 NVIDIA 显卡,并正确安装 NVIDIA 显卡驱动程序、CUDA Toolkit 和 cuDNN.

3.4.1 GPU 硬件的准备

TensorFlow 对 NVIDIA 显卡的支持较为完备。对于 NVIDIA 显卡,要求其 CUDA 的算力(compute capability) 不低于 3.5。 可以到 NVIDIA 的官网查询自己所用显卡的 CUDA 算力。

目前 AMD 显卡也开始对 TensorFlow 提供支持。

3.4.2 NVIDIA 驱动程序的安装
  • Windows 下安装 NVIDIA 驱动程序:

    • 在 Windows 系统中,如果系统具有 NVIDIA 显卡,那么系统内往往已经自动安装了 NVIDIA 显卡驱动程序。 如果未安装,直接访问 NVIDIA 官网,下载并安装对应型号的最新标准版驱动程序即可。
  • Linux 下安装 NVIDIA 驱动程序:

    • 服务器版 Linux 系统

      1.访问 NVIDIA 官网下载驱动程序(.run 文件)

      1. 安装驱动

        sudo apt-get install build-essential # 安装之前,可能需要安装合适的编译环境
        sudo bash DRIVER_FILE_NAME.run
        
    • 具有图形界面的桌面版 Linux 系统(Ubuntu为例)

      1.禁用系统自带的开源显卡驱动 Nouveau(在 /etc/modprobe.d/blacklist.conf)文件中添加如下内容,并更新内核、重启

      cd /etc/modprobe.d/blacklist.conf
      blacklist nouveau
      sudo update-initramfs -u
      

      2.禁用主板的 Secure Boot 功能 3.停用桌面环境

      sudo service lightdm stop
      

      4.删除原有 NVIDIA 驱动程序

      sudo apt-get purge nvidia*
      
  • NVIDIA 驱动程序安装完成后,可以在命令行下使用 nvidia-smi 命令检查是否安装成功,若成功,则会打印当前系统安装的 NVIDIA 驱动信息:

    $ nvidia-sim
    

    Note

    nvidia-sim 命令可以产看机器上现有的 GPU 及使用情况

3.4.3 CUDA Toolkit 和 cuDNN 的安装
  • 在 Anaconda/Miniconda 环境下,推荐使用 conda 安装 CUDA Toolkit 和 cuDNN

    1.搜索 conda 源中可用的 CUDA Toolkit 和 cuDNN 版本号

    conda search cudatoolkit
    conda search cudnn
    

    2.安装 CUDA Toolkit 和 cuDNN

    conda install cudatoolkit=X.X
    conda install cudnn=X.X.X
    
  • 在 使用 Python pip 安装 时:

    ...
    
  • 按照 TensorFlow 官网的说明手动下载 CUDA Toolkit 和 cuDNN 并安装,不过过程比较繁琐。

Keras Env

Keras comes packaged with TensorFlow 2.0 as tensorflow.keras. To start using Keras, simply install TensorFlow 2.0.

  • Keras/Tensorflow 兼容的设备
    • Python 3.5-3.8
    • Ubuntu 16.04 or later
    • Windows 7 or later
    • macOS 10.12.6(Sierra) or later
  • 在安装 Keras 之前, 需要安装一个后端引擎: TensorFlow, Theano, CNTK
  • 安装可选依赖项
    • cuDNN(如果您计划在GPU上运行Keras, 建议使用)
    • HDF5和h5py(如果您计划将Keras模型保存到磁盘, 则需要)
    • graphviz和pydot(由可视化实用程序用于绘制模型图)
  • 安装 Keras
  • 配置 Keras 后端

1. 安装后端引擎

Tensorflow 2.0:

# Tensorflow GPU version
pip install --upgrade tensorflow-gpu

# Tensorflow CPU version
pip install --upgrade tensorflow

Theano:

$ todo

CNTK:

$ todo

2. 安装可选依赖项

cUNN:

HDF5,h5py:

$ conda install h5py

or

$ pip install h5py

graphviz:

$ pip install

pydot:

$ pip install

3. 安装 Keras

从 PYPI 安装 Keras:

# keras nonvirtualenv version
$ sudo pip install keras

# keras virtualenv version
$ pip install keras

# tensorflow version
$ pip install keras -U --pre

从 GitHub 源安装 Keras:

$ cd /usr/local
$ git clone https://github.com/keras-team/keras.git
$ cd keras
$ sudo python setup.py install

4. 配置 Keras 后端

默认情况下, Keras 将使用 Tensorflow 作为其张量操作库, 配置步骤如下:

配置文档

PyTorch Env

1.PyTorch 支持的硬件平台

  • PC

    • CPU
    • GPU
    • Cloud TPU(张量)
  • Mobile

    • Android
    • iOS
    • Embedded Devices

2.PyTorch 系统要求

_images/torch_install.png
  • Linux distributions that use glibc >= v2.17

    • Arch Linux, minimum version 2012-07-15
    • CentOS, minimum version 7.3-1611
    • Debian, minimum version 8.0
    • Fedora, minimum version 24
    • Mint, minimum version 14
    • OpenSUSE, minimum version 42.1
    • PCLinuxOS, minimum version 2014.7
    • Slackware, minimum version 14.2
    • Ubuntu, minimum version 13.04
  • macOS 10.10(Yosemite) or above

  • Windows

    • Windows 7 and greater; Windows 10 or greater recommended.
    • Windows Server 2008 r2 and greater

3.macOS 安装 PyTorch

3.1 使用 pip 安装 PyTorch

$ pip install numpy
$ pip install torch torchvision

3.2 使用 Anaconda 安装 PyTorch

$ conda install pytorch torchvision -c pytorch

3.3 使用 Docker 安装 PyTorch

$ test test

4.Ubuntu 安装 Pytorch

4.1 使用 pip 安装 PyTorch

$ pip install torch==1.8.0+cpu torchvision==0.9.0+cpu torchaudio==0.8.0 -f https://download.pytorch.org/whl/torch_stable.html

4.2 使用 Anaconda 安装 PyTorch

$ conda install pytorch torchvision torchaudio cpuonly -c pytorch

4.3 使用 Docker 安装 PyTorch

$ test test

5.Building from source

5.1 macOS

  • Prerequisites
    • Install Anaconda
    • Install CUDA, if your machine has a CUDA-enabled GPU.
    • Install optional dependencies:
$ export CMAKE_PREFIX_PATH=[anaconda root directory]
$ conda install numpy pyyaml mkl mkl-include setuptools cmake cffi typing
$ git clone --recursive https://github.com/pytorch/pytorch
$ cd pytorch
$ MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py install

Note

当前,仅可以通过从源码构建 PyTorch 来获得 macOS 的 CUDA 支持

5.2 Ubuntu

$ git clone

6.Verification

  • Torch 使用:

    >>> from __future__ import print_function
    >>> import torch
    
    >>> x = torch.rand(5, 3)
    >>> print(x)
    
    tensor([[0.3380, 0.3845, 0.3217],
          [0.8337, 0.9050, 0.2650],
          [0.2979, 0.7141, 0.9069],
          [0.1449, 0.1132, 0.1375],
          [0.4675, 0.3947, 0.1426]])
    
  • GPU dirver 和 CUDA:

    import torch
    
    torch.cuda.is_available()
    

DNN

1. 机器学习与深度学习

机器学习 就是从历史数据中探索和训练出数据的普遍规律, 将其归纳为相应的数学模型, 并对未知的数据进行预测的过程; 在这个过程中会碰到各种各样的问题, 比如如下等一系列关乎机器学习模型生死的问题:

  • 数据质量
  • 模型评价标准
  • 训练优化方法
  • 过拟合

在机器学习中, 有很多已经相当成熟的模型, 在这些机器学习模型中, 人工神经网络 就是一种比较厉害的模型; 人工神经网络从早期的感知机发展而来, 对任何函数都有较好的拟合性.

但自上个世纪 90 年代一直到 2012 年深度学习集中爆发前夕, 神经网络受制于计算资源的限制和较差的可解释性, 一直处于发展的低谷阶段. 之后大数据兴起,计算资源也迅速跟上, 加之 2012 年 ImageNet 竞赛冠军采用的 AlexNet 卷积神经网络一举将图片预测的 top5 错误率降至 16.4%, 震惊了当时的学界和业界. 从此之后, 原本处于研究边缘状态的神经网络又迅速热了起来, 深度学习也逐渐占据了计算机视觉的主导地位.

以神经网络为核心的深度学习理论是机器学习的一个领域分支, 所以深度学习其本质上也必须遵循一些机器学习的基本要以和法则.

2.感知机

2.1 感知机模型介绍

2.1.1 感知机模型

感知机, perceptron 是由美国学者 Frank Rosenblatt 在 1957 年提出来的, 感知机是神经网络(深度学习)的起源算法. 因此, 学习感知机的构造也就是学习通向神经网络和深度学习的一种重要思想, 感知机是神经网络的理论基础.

感知机就是一个通过建立一个线性超平面, 对线性可分的数据集进行分类的线性模型. 感知机接收多个输入信号, 输出一个信号.

假设 \(x_1, x_2, \cdots, x_p\) 是输入信号, \(\hat{y}\) 是输出信号, \(w_1,w_2,\cdots, w_p\) 是权重, \(b\) 是偏置.输入信号被送往神经元时, 会被分别乘以固定的权重 \((w_1x_1,w_2x_2,\cdots,w_px_p)\). 神经元会计算传送过来的信号的总和, 只有当这个总和超过了某个界限值时, 才会输出 1. 这也被称为”神经元被激活”. 这里将这个界限值称为阈值, 用符号 \(\theta\) 表示.

感知机的多个输入信号都有各自固定的权重, 这些权重发挥着重要控制各个信号的重要性的作用. 也就是说,权重越大, 对应该权重的信号的重要性就越高:

\[\hat{y}=\sigma\Big(\sum_{i=1}^{p} \omega_i x_i + b\Big)\]
\[\begin{split}\begin{cases} y = 1, \text{if} (\sum_{i=1}^{p} \omega_{i} x_{i} + b) > \theta \\ y = 0, \text{if} (\sum_{i=1}^{p} \omega_{i} x_{i} + b) \leq \theta \end{cases}\end{split}\]
2.1.2 感知机训练
  • 首先, 模型接受输入 \(x_{i}\),将输入 \(x_{i}\) 与权重(weights) \(\omega_i\) 和偏置(bias) \(b\) 进行加权求和 \(\sum_{i=1}^{p} \omega_i x_i + b\), 并经过 \(\sigma(\cdot)\) 函数进行激活,将激活结果作为 \(\hat{y}\) 进行输出. 这便是感知机执行前向传播计算的基本过程;
  • 其次, 当执行完前向传播计算得到输出 \(\hat{y}\) 之后, 模型需要根据输出 \(\hat{y}\) 和实际的样本标签(sample label) \(y\) 按照损失函数 \(L(y, \hat{y})\) 计算当前损失;
  • 最后, 通过计算损失函数 \(L(y, \hat{y})\) 关于权重(weights) \(\omega_i\) 和偏置(bias) \(b\) 的梯度, 根据梯度下降算法更新权重和偏置. 经过不断的迭代调整权重和偏置使得损失最小, 这便是完整的 单层感知机 的训练过程.

2.2 单层感知机的局限性

  • 单层感知机无法分离非线性空间

    • 单层感知机只能表示由一条直线(超平面)分割的空间
  • 感知机无法实现异或门(XOR gate)

    • XOR 函数(“异或”逻辑)是两个二进制值的运算, 当这些二进制值中恰好有一个为 1 时, XOR 函数返回值为 1, 其余情况下为 0.

2.3 多层感知机可以实现异或门

  • 感知机可以通过叠加层可以表示异或门, 实现非线性空间的分割:

    • 异或门 可以通过组合 与非门或门, 再将前两个逻辑门的组合和 与门 组合得到
  • 叠加了多层的感知机也称为 多层感知机(MLP, Multi-Layered Perceptron)

2.4 从感知机到神经网络

  • 单层感知机 包含两层神经元,即输入与输出神经元,可以非常容易的实现逻辑与、或和非等线性可分情形, 但终归而言,这样的一层感知机的学习能力是非常有限的, 对于像异或这样的非线性情形, 单层感知机就搞不定了. 其学习过程会呈现一定程度的振荡,权值参数 \(\omega_i\) 难以稳定下来,最终不能求得合适的解;

  • 对于 非线性可分 的情况, 在感知机的基础上一般有了两个解决方向:

    1. 支持向量机模型
      • 支持向量机旨在通过 核函数 映射来处理非线性的情况;
    2. 神经网络模型
      • 神经网络模型也叫 多层感知机, 与单层的感知机 在结构上的区别主要在于 MLP 多了若干 隐藏层, 这使得神经网络对非线性的情况拟合能力大大增强.

3.深度前馈网络

3.1 深度前馈网络概念

  • 深度学习模型:

    • 前馈神经网络 (Feedforward Neural Network)
      • 深度前馈网络 (Deep Feedforward Network)
      • 多层感知机 (Multilayer Perceptron, MLP)
    • 反馈神经网络 (FeedBack Neural Network)
      • 循环神经网络 (Recurrent Neural Network)
  • 神经网络结构:

    • 深度
    • 宽度
    • 第一层,第二层,…
    • 隐藏层
    • 输出层
  • 深度前馈网络介绍:

    • 深度前馈网络的目标是: 近似某个函数 \(f^{*}\).

      • 深度前馈网络定义了一个映射 \(y = f(x; \theta)\), 并且学习参数 \(\theta\) 的值, 使它能够得到最佳的函数近似 \(f^{*}\).
    • 深度前馈网络之所以被称为 前馈(feedforward) 的, 是因为信息流过 \(x\) 的函数, 流经用于定义 \(f\) 的中间计算过程, 最终到达输出 \(y\). 在模型的输出和模型本身之间没有 反馈(feedback) 连接. 当深度前馈网络被扩展成包含反馈连接时, 被称为 循环神经网络(Recurrent Reural Network, RNN).

    • 深度前馈网络之所以被称为 网络(network), 是因为它们通常用许多不同函数复合在一起来表示, 该模型与一个有向无环图相关联, 而图描述了函数是如何复合在一起的. 网络链的全长称为模型的 深度(depth)

      \[f(x) = f^{(3)}(f^{(2)}(f^{(1)}(x)))\]
      • 其中:

        • \(f^{(1)}\): 网络的第一层(first layer)

        • \(f^{(2)}\): 网络的第二层(second layer)

          • 隐藏层(hidden layer)
        • \(f^{(3)}\): 网络的输出层(output layer)

    • 深度前馈网络之所以被称为 神经网路, 是因为他们或多或少地受到神经科学的启发.

      • 网络中每个隐藏层通常都是向量值的. 这些隐藏层的维数决定了模型的 宽度(width). 向量的每个元素都可以被视为起到类似一个神经元的作用. 除了将层想象成向量到向量的单个函数, 也可以把层想象成由许多并行操作 单元(unit) 组成, 每个单元表示一个向量到标量的函数. 每个单元在某种意义上类似一个神经元, 它接收的输入来源于许多其他的单元, 并计算自己的激活值.
  • 深度前馈网络设计:

    • 选择优化模型

    • 选择代价函数

    • 选择输出单元形式

    • 选择用于计算隐藏层值激活函数(activation function)

    • 设计网络的结构, 包括

      • 网络应该包含多少层
      • 层与层之间应该如何连接
      • 每一层包含多少单元
    • 反向传播(back propagation)算法和推广

3.2 线性模型的局限性及克服

  • 线性模型的局限性:

    • 线性模型,如逻辑回归和线性回归, 是非常吸引人的, 因为无论是通过闭解形式还是使用凸优化, 它们都能高效且可靠地拟合. 线性模型也有明显的缺陷: 模型的能力被局限在线性函数里, 所以无法理解任何两个输入变量之间的相互作用.
  • 克服线性模型的局限性:

    • 为了扩展线性模型来表示 \(x\) 的非线性函数,可以不把线性模型用于 \(x\) 本身,而是用在一个变换后的输入 \(\phi(x)\) 上,这里 \(\phi\) 是一个非线性学习算法,可以认为 \(\phi\) 提供了一组描述 \(x\) 的特征,或者认为它提供了 \(x\) 的一个新的表示.
  • 如何选择映射 \(\phi\)?

    1. 其中一种选择是使用一个通用的 \(\phi\), 例如无限维的 \(\phi\), 它隐含地用在基于 RBF 核的核机器上.

    2. 另一种选择是手动设计 \(\phi\), 传统的机器学习模型.

    3. 深度学习的策略是去学习 \(\phi\). 在这种方法中, 有一个模型 \(y=f(x;\theta,\omega)= \phi(x;\theta)^{T}\omega\), 现在有两种参数:

      • 用于从一大类函数中学习 \(\phi\) 的参数 \(\theta\)

      • 用于将 \(\phi(x)\) 映射到所需的输出的参数 \(\omega\).

        • \(\phi\) 定义了一个隐藏层, 即: 通过学习特征来改善模型.

4.基于梯度的学习

线性模型和神经网络的最大区别, 在于神经网络的非线性导致大多数感兴趣的代价函数都变得非凸, 这意味着神经网络的训练通常使用迭代的、 基于梯度的优化, 仅仅使得代价函数达到一个非常小的值;而不是像用于训练线性回归模型的线性方程求解器, 或者用于训练逻辑回归或 SVM 的 凸优化算法那样保证全局收敛。

凸优化从任何一种初始参数出发都会收敛(理论上如此, 在实践中也很鲁棒但可能会遇到数值问题)。用于非凸损失函数的随机梯度下降没有这种收敛性保证, 并且对参数的初始值很敏感。

对于前馈神经网络, 将所有的权重值初始化为小随机数是很重要的。偏置可以初始化为 0 或者小的正值。

Note

  • 当然可以用梯度下降来训练诸如线性回归和 SVM 之类的模型, 但是事实上, 当训练集相当大时这是很常用的。从这点来看, 训练神经网络和训练其他任何模型并没有太大的区别。只是, 计算梯度对于神经网络会略微复杂一些, 但仍然可以很高效而精确地实现。

4.1 代价函数/损失函数

为了使用基于梯度的学习方法, 必须选择一个代价函数, 并且必须选择如何表示模型的输出。

  • 大多数情况下, 参数模型定义了一个分布 \(p(y|x;\theta)\) 并且简单地使用最大似然原理。这意味着使用训练数据和模型预测间的交叉熵作为代价函数。
  • 有时, 使用一个更加简单的方法, 不是预测 \(y\) 的完整概率分布, 而是仅仅预测在给定 \(x\) 的条件下 \(y\) 的某种统计量。 某些专门的损失函数允许我们来训练这些估计量的预测器。

Note

  • 用于训练神经网络的完整的代价函数, 通常在基本代价函数的基础上集合一个正则项。

4.2 输出层的设计

  • 神经网络可以用在分类和回归问题上, 不过需要根据情况改变输出层的激活函数
  • 一般而言,回归问题用 恒等函数, 分类问题用 softmax 函数
4.2.1 输出层激活函数
4.2.1.1 恒等函数
  • 恒等函数的形式

    \[\sigma(x) = x\]
4.2.1.2 Softmax 函数
  • softmax函数的形式

    \[y_k = \frac{e^{a_{k}}}{\sum_{i=1}^{n}e^{a_i}}\]
    • 其中:

      • \(n\): 是输出层神经元的个数
      • \(k\): 是指第 \(k\) 个神经元
      • \(a\): 是输入信号
  • softmax函数针对 溢出 问题的改进

    \[y_k = \frac{e^{a_k+C}}{\sum_{n}^{i=1}e^{a_i+C}}\]
4.2.2 输出层的神经元数量
  • 输出层的神经元数量需要根据待解决的问题决定
  • 对于分类问题, 输出层的神经元数量一般设定为类别的数量

5.隐藏单元

5.1 隐藏单元设计介绍

  • 隐藏单元的设计方法

    • 隐藏单元的设计是一个非常活跃的研究领域, 并且还没有许多明确的指导性理论原则。
    • 整流线性单元(ReLU)是隐藏单元极好的默认选择。许多其他类型的隐藏单元也是可用的, 决定何时使用哪种类型的隐藏单元是困难的事, 通常不可能预先预测出哪种隐藏单元工作得最好。
    • 隐藏单元的设计过程充满了试验和错误, 先直觉认为某种隐藏单元可能表现良好, 然后用它组成神经网络进行训练, 最后用验证集来评估它的性能。
  • 隐藏层函数可微性分析

    • 一些隐藏单元可能并不是在所有的输入点上都是可微的。

      • 在实践中, 梯度下降对这些机器学习模型仍然表现得足够好, 部分原因是神经网络训练算法通常不会达到代价函数的局部最小值, 而是仅仅显著地减小它的值
    • 不可微的隐藏单元通常只在少数点上不可微。

      • 一般来说, 函数 \(g(x)\) 具有左导数和右导数, 左导数定义为紧邻在 \(z\) 左边的函数的斜率, 右导数定义为紧邻在 \(z\) 右边的函数的斜率。只有当函数在 \(z\) 处的左导数和右导数都有定义并且相等时, 函数在 \(z\) 点处才是可微的。
    • 在实践中, 可以放心地忽略隐藏单元激活函数的不可微性

      • 除非另有说明, 大多数的隐藏单元都可以描述为接受输入向量 \(x\) , 计算仿射变换 \(z=W^{T}x+b\) , 然后使用一个逐元素的非线性函数 \(g(z)\)。 大多数隐藏单元的区别仅仅在于激活函数 \(g(z)\) 的形式。

Note

神经网络中用到的函数通常对左导数和右导数都有定义, 软件实现通常返回左导数或右导数的其中一个, 而不是报告导数未定义或产生一个错误。

5.2 隐藏层激活函数

5.1.1 ReLU

在神经网络发展的历史上, Sigmoid 激活函数很早就开始使用了, 而在现代神经网络中, 默认推荐的是使用 ReLU(Rectified Linear Unitm 整流线性单元) 函数(Jarrett et al., 2009b; Nair and Hinton, 2010a;Glorot et al., 2011a).

\[\begin{split}h(x)=max\{0, x\} = \left \{ \begin{array}{rcl} x & & {x > 0} \\ 0 & & {x \leq 0} \\ \end{array} \right.\end{split}\]

Note

  • 整流线性激活函数 (ReLU) 是被推荐用于大多数前馈神经网络的默认激活函数。将此函数用于线性变换的输出将产生非线性变换。 然而, 函数仍然非常接近线性, 在这种意义上它是具有两个线性部分的分段线性函数。
  • 由于 ReLU 几乎是线性的, 因此他们保留了许多使得线性模型易于使用基于梯度的方法进行优化的属性。 它们还保留了许多使得线性模型能够泛化良好的属性。
  • 计算机科学的一个公共原则是, 可以从最小的组件构建最复杂的系统, 就像图灵机的内存只需要能够存储 0 或 1 的状态, 可以从整流线性函数构建一万个函数近似器
5.1.2 Sigmoid

神经网络中用 Sigmoid 函数作为激活函数, 进行信号的转换, 转换后的信号被传送给下一个神经元.

\[h(x) = \frac{1}{1+e^{-x}}, 其中: e是纳皮尔常数 2.7182...\]
5.1.3 双曲正切函数
\[h(x) = tanh(x) =\]
5.1.4 其他激活函数

反向传播算法

1.计算图(computational graph)

计算图将计算过程用图形表示出来,这里的图形是指数据结构图,通过多个节点和边表示

用计算图解题时,需要按如下流程进行:

  • 构建计算图;
  • 在计算图上从左到右进行计算(正向传播, forward propagation);
  • 在计算图上从右到左进行计算(反向传播, backward propagation);
    • 将上游传过来的值E乘以节点的局部导数 \(\frac{\partial y}{\partial x}\),然后将结果传递给下一个节点

计算图的优点:

  • 局部计算;
    • 无论全局多么复杂的计算,都可以通过局部计算使各个节点致力于简单的计算,从而简化问题;
  • 利用计算图可以将中间的计算结果保存起来;
  • 可以通过反向传播高效计算导数;
  • 综上:可以通过正向传播和反向传播高效地计算各个变量的导数值;

2.链式法则(chain rule)

计算图的正向传播将计算结果正向(从左到右)传递,而反向传播将局部导数向正方向的反方向(从右到左)传递;

复合函数:

  • 复合函数是由多个函数构成的函数;

链式法则:

  • 如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示;

3.反向传播

  • 加法节点的反向传播
    • 加法的反向传播只是将上游的值传给下游,并不需要正向传播的输入信号;
  • 乘法节点的反向传播
    • 乘法的反向传播会将上游的值乘以正向传播时的输入信号的“翻转值”后传递给下游,翻转值表示一种翻转关系;
    • 乘法的反向传播需要正向传播时的输入信号值;因此,实现乘法节点的反向传播时,要保存正向传播的输入信号;

4 梯度爆炸与梯度消失

4.1 梯度爆炸与梯度消失的问题提出

  • 在神经网络训练的过程中,当网络结构变深时,神经网络在训练时会碰到梯度爆炸或者梯度消失的情况。 那么什么是梯度爆炸和梯度消失呢?它们又是怎么产生的呢?

4.2 梯度爆炸与梯度消失现象解释

  • 由于神经网络的训练机制,不管是哪种类型的神经网络,其训练都是通过反向传播计算梯度来实现权重更新的。 我们通过设定损失函数,建立损失函数关于各层网络输入输出的梯度计算,当网络训练开动起来的时候, 系统便按照反向传播机制来不断更新网络各层参数直到停止训练。但当网络层数加深时,这个训练系统并不是很稳, 经常会出现一些问题。其中梯度爆炸和梯度消失便是最大的问题之二。
  • 梯度爆炸
    • 在神经网络训练过程中,梯度变得越来越大,使得神经网络权重得到疯狂更新的情形,这种情况很容易发现, 因为梯度过大,计算更新得到的参数也会大到崩溃,这时候我们可能看到更新的参数值中有很多的 NaN, 这说明梯度爆炸已经使得参数更新出现数值溢出
  • 梯度消失
    • 与梯度爆炸相反的是,梯度消失就是在神经网络训练过程中梯度变得越来越小以至于梯度得不到更新的一种情形。 当网络加深时,网络深处的误差因为梯度的减小很难影响到前层网络的权重更新,一旦权重得不到有效的更新计算, 神经网络的训练机制也就失效了

4.3 梯度爆炸和梯度消失出现的机制

  • 神经网络训练过程中梯度怎么就会变得越来越大或者越来越小呢?以神经网络反向传播推导公式为例来解释:

    _images/gradient_explosion_disappear.png
  • 上述公式是一个两层网络的反向传播参数更新公式推导过程。离输出层相对较远的是输入到隐藏层的权重参数, 可以看到损失函数对于隐藏层输出 a1 输入到隐藏层权重 W1 和偏置 b1 的梯度计算公式,一般而言都会转 换从下一层的权重乘以激活函数求导后的式子。如果激活函数求导后的结果和下一层权重的乘积大于 1 或者 说远远大于 1 的话,在网络层数加深时,层层递增的网络在做梯度更新时往往就会出现梯度爆炸的情况。 如果激活函数求导和下一层权重的乘积小于 1 的话,在网络加深时,浅层的网络梯度计算结果会越来越小往往 就会出现梯度消失的情况。所以可是说是反向传播的机制本身造就梯度爆炸和梯度消失这两种不稳定因素。 比如说一个 100 层的深度神经网络,假设每一层的梯度计算值都为 1.1,经过由输出到输入的反向传播梯度计 算可能最后的梯度值就变成 1.1^100 = 13780.61234,这是一个极大的梯度值了,足以造成计算溢出问题。 若是每一层的梯度计算值为 0.9,反向传播输入层的梯度计算值则可能为 0.9^100 = 0.000026561398, 足够小到造成梯度消失。(例子只是一个简化的假设情况,实际反向传播计算要更为复杂。)

所以总体来说,神经网络的训练中梯度过大或者过小引起的参数过大过小都会导致神经网络失效,那我们的目的就 是要让梯度计算回归到正常的区间范围,不要过大也不要过小,这也是解决这两个问题的一个思路。 下图是一个 4 层网络的训练过程各参数的取值范围:

4.4 梯度爆炸与梯度消失的解决方法

  • 那么梯度爆炸和梯度消失怎么解决呢?梯度爆炸并不麻烦,在实际训练的时候对梯度进行修剪即可,但是梯度消失的处 理就比较麻烦了,由上述的分析我们知道梯度消失一个关键在于激活函数。sigmoid 激活函数本身就更容易产生这种幺蛾子, 所以一般而言,我们换上更加鲁棒的 ReLu 激活函数以及给神经网络加上归一化激活函数层,一般问题都能得到很好的解决, 但也不是任何情形下都管用,比如说咱们的 RNN 网络。

优化算法

1.神经网络模型学习算法优化

深度学习算法在很多种情况下都涉及优化。在深度学习涉及的诸多优化问题中,最难的是神经网络训练, 这里的学习算法优化就特指神经网络训练中的优化问题:寻找神经网络上的一组参数 \(\theta\), 它能显著地降低代价函数 \(J(\theta) = E_{(x, y) \backsim \hat{p}_{data}}L(f(x;\theta), y)\), 该代价函数通常包括整个训练集上的性能评估和额外的正则化项.

1.1 经验风险最小化

1.2 代理损失函数和提前终止

1.3 批量算法和小批量算法

  1. 梯度下降算法(Gradient Descent, GD)
  2. 小批量梯度下降算法(mini-batch Gradient Descent)
    • 在实际的数据应用场景,直接对大批量数据执行梯度下降法训练模型时,机器处理数据的速度会非常缓慢, 这时将训练数据集分割成小一点的子集进行多次训练非常重要。这个被分割成的小的训练数据子集就做 mini-batch, 意为小批量。对每个小批量数据集同时执行梯度下降法会大大提高训练效率。在实际利用代码实现的时候, 小批量梯度下降算法通常包括两个步骤:充分打乱数据(shuffle)和分组组合数据【分区】(partition)
  3. 随机梯度下降算法(Stochastic Gradient Descent, SGD)
  4. 带动量的梯度下降算法(Gradient Descent with Momentum)
  5. 自适应矩估计(Adaptive Moment Estimation, Adam)
  6. 加速梯度下降算法(RMSprop)

2.神经网络优化中的挑战

2.1 病态

2.2 局部最小值

2.3 高原、鞍点和其他平坦区域

2.4 悬崖和梯度爆炸

2.5 长期依赖

2.6 非精确梯度

2.7 局部和全局结构间的弱对应

2.8 优化的理论限制

3.梯度算法

机器学习和神经网络的学习都是从训练数据集中学习时寻找最优参数(权重和偏置),这里的最优参数就是损失函数取最小值时的参数。 一般而言,损失函数很复杂,参数空间庞大,通过使用梯度来寻找损失函数最小值(或尽可能小的值)的方法就是 梯度法

  • 梯度表示的是一个函数在各点处的函数值减小最多的方向,因此,无法保证梯度所指的方向就是函数的最小值或者真正应该前进的方向。实际上在复杂的函数中,梯度指示的方向基本上都不是函数值最小处
    • 函数的极小值,最小值,鞍点(saddle point)的地方,梯度为0
      • 最小值是指全局最小值
      • 极小值是指局部最小值,也就是限定在某个范围内的最小值
      • 鞍点是从某个方向上看是极大值,从另一个方向上看则是极小值的点;
  • 虽然梯度的方向并不一定指向最小值,但沿着它的方向能够最大限度地减小函数的值。因此,在寻找函数的最小值(或者尽可能小的值)的位置的任务中,要以梯度的信息为线索,决定前进的方向

3.1 梯度算法

在梯度算法中,函数的取值从当前位置沿着梯度方向前进一段距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿着梯度方向前进,逐渐减小函数的值,梯度算法可以分为两种,但常用的是梯度下降算法:

  • 梯度下降算法(gradient descent method)
  • 梯度上升算法(gradient ascent method)

梯度下降法算法的数学表示:

\(\omega_i^{(t)} = \omega_i^{(t)} - \eta^{(t)} \frac{\partial l}{\partial \omega_i^{(t)}}\)

其中:

  • \(\omega_i^{(t)}\):在第 \(t\) 轮迭代时的第 \(i\) 个参数;
  • \(l\):损失函数;
  • \(\eta^{(t)}\):第 \(t\) 轮迭代时的学习率 (learning rate),决定在一次学习中,应该学习多少,以及在多大程度上更新参数;实验表明,设定一个合适的 learning rate 是一个很重要的问题:
    • 学习率过大,会发散成一个很大的值;
    • 学习率过小,基本上没怎么更新就结束了;

3.2 神经网络学习算法

神经网络的学习步骤:

  • 前提: - 神经网络存在合适的权重和偏置,调整权重和偏置以便你和训练数据的过程称为“学习”
  • 步骤1:mini-batch - 从训练数据中随机选出一部分数据,这部分数据称为mini-batch。目标是减小 mini-batch 的损失函数的值;
  • 步骤2:计算梯度 - 为了减小 mini-batch 的损失函数的值,需要求出各个权重参数的梯度;梯度表示损失函数的值减小最多的方向;
  • 步骤3:更新参数 - 将权重参数沿梯度方向进行微小更新;
  • 步骤4:(重复) - 重复步骤 1,步骤 2,步骤 3

梯度算法(Gradient Descent, GD)改进:

在深度学习实际的算法调优中,原始的梯度下降法一般不大好用。通常来说,工业环境下深度学习所处理的数据量都是相当大的。 这时若直接使用原始版本的梯度下降,可能训练速度和运算效率会非常低。这时候就需要采取一些策略对原始的梯度下降法进行调整来加速训练过程。

  • 改进1:
    • 随机梯度下降:从 GD mini-batch GD, SGD
    • mini-batch GD:将训练数据划分为小批量(mini-batch)进行训练 - 将训练集划分为一个个子集的小批量数据,相较于原始的整体进行梯度下降的方法,整个神经网络的训练效率会大大提高
    • SGD:如果批量足够小,小到一批只有一个样本,这时算法就是随机梯度下降(SGD)
      • 使用随机梯度下降算法,模型训练起来会很灵活,数据中的噪声也会得到减小,但是随机梯度下降会有一个劣势就是失去了向量化运算带来的训练加速度, 算法也较难收敛,因为一次只处理一个样本,虽然足够灵活但效率过于低下
    • 在深度学习模型的实际处理中,选择一个合适的batch-size 是一个比较重要的问题
      • 一般而言需要视训练的数据量来定,也需要不断的试验
        • 通常而言,batch-size 过小会使得算法偏向 SGD 一点,失去向量化带来的加速效果,算法也不容易收敛
        • 但若是盲目增大 batch-size,一方面会比较吃内存,另一方面是梯度下降的方向很难再有变化,进而影响训练精度
        • 所以一个合适的 batch-size 对于深度学习的训练来说就非常重要,合适的 batch-size 会提高内存的利用率, 向量化运算带来的并行效率提高,跑完一次 epoch 所需要的迭代次数也会减少,训练速度会加快。这便是小批量(mini-batch)梯度下降 batch-size 的作用
    • 无论是梯度下降法(GD)、小批量(mini-batch)梯度下降法还是随机梯度下降法(SGD),它们的本质都是基于梯度下降的算法策略,三者的区别即在于执行一次运算所需要的样本量
  • 改进2:
    • 动量梯度下降:从 Momentum 到 Adam
    • Momentum GD:基于移动加权的思想,给梯度下降加上历史梯度的成分来进一步加快下降速度,这种基于历史梯度和当前梯度进行加权计算的梯度下降法便是动量梯度下降法(Momentum GD) - 动量梯度下降算法公式

3.3 基本算法

3.3.1 SGD
  • SGD的数学表示:

    \[W \leftarrow W - \eta \frac{\partial L}{\partial W}\]
    • 其中:
      • \(W\): 需要更新的权重参数
      • \(\frac{\partial L}{\partial W}\): 损失函数关于权重参数 \(W\) 的梯度
      • \(\eta\): 学习率(learning rate),事先决定好的值,比如: 0.001, 0.01
  • SGD的Python实现:

    class SGD:
       def __init__(self, lr = 0.01):
          self.lr = lr
    
       def update(self, params, grads):
          for key in params.keys():
             params[key] -= self.lr * grads[key]
    
  • SGD的缺点:

    • 低效
      • 如果损失函数的形状非均向(anisotropic),比如呈延伸状,搜索的路径就会非常低效
      • SGD低效的根本原因是:梯度的方向并没有指向最小值的方向
3.3.2 Momentum SGD(动量随机梯度下降法)
  • Momentum SGD的数学表示:

    \[\upsilon \leftarrow \alpha \upsilon - \eta \frac{\partial L}{\partial W}\]
    \[W \leftarrow W + \upsilon\]

    其中:

    • \(W\): 需要更新的权重参数
    • \(\frac{\partial L}{\partial W}\): 损失函数关于权重参数 \(W\) 的梯度
    • \(\eta\): 学习率(learning rate),事先决定好的值,比如:0.001, 0.0
    • \(\upsilon\): 对应物理上的速度,\(\upsilon\) 的更新表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加
    • \(\alpha \upsilon\): 对应了物理上的地面摩擦或空气阻力,表示在物体不受任何力时,该项承担使物体逐渐减速的任务(\(\alpha\) 一般设定为0.9)
  • Momentum SGD的Python实现:

    class Momentum:
       def __init__(self, lr = 0.01, momentum = 0.9):
          self.lr = lr
          self.momentum = momentum
          self.v = None
    
       def update(self, params, grads):
          if self.v is None:
             self.v = {}
             for key, val in params.items():
                self.v[key] = np.zeros_like(val)
    
          for key in params.keys():
             self.v[key] = self.momentum * self.v[key] - self.lr * self.grads[key]
             params[key] += self.v[key]
    
  • Momentum SGD的优缺点:

    • 为什么会好?
3.3.3 Nesterov 动量

3.4 自适应学习率算法

3.4.1 AdaGrad
  • 在神经网络的学习中,学习率(learning rate, lr or \(\eta\))的值很重要。
  • 学习率过小,会导致学习花费过多的时间;
  • 学习率过大,会导致学习发散而不能正确进行;
  • 学习率衰减(learning rate decay):随着学习的进行,使学习率逐渐减小;
  • 逐渐减小学习率的想法,相当于将“全体”参数的学习率一起降低;
  • AdaGrad发展了学习率衰减的想法,针对一个一个的参数,赋予其“定制”的值;
  • AdaGrad会为参数的每个元素适当地调整学习率,与此同时进行学习;
  • AdaGrad SGD的数学表示:

    \[h \leftarrow h + \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W}\]
    \[W \leftarrow W - \eta \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W}\]

    其中:

    • \(W\): 需要更新的权重参数;
    • \(\frac{\partial L}{\partial W}\): 损失函数关于权重参数 \(W\) 的梯度;
    • \(\eta\): 学习率(learning rate),事先决定好的值,比如:0.001, 0.01;
    • \(h\): 保存了以前所有梯度值的平方和,然后,在更新参数时,通过乘以\(\frac{1}{\sqrt{h}}\)就可以调整学习的尺度 - 参数的元素中变动较大(被大幅更新)的元素的学习率将变小,也就是说,可以按照参数的元素进行学习率衰减,使变动的参数的学习率逐渐减小;
  • AdaGrad SGD的Python实现:

    class AdaGrad:
       def __init__(self, lr = 0.01):
          self.lr = lr
          self.h = None
    
       def update(self, params, grads):
          if self.h is None:
             self.h = {}
             for key, val in params.items():
                self.h[key] = np.zeros_like(val)
    
          for key in params.keys():
             self.h[key] += grads[key] * grads[key]
             param[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
    
  • AdaGrad SGD的优缺点:

    • AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为0,完全不再更新。
    • 为了改善这个问题,可以使用RMSProp方法。RMSProp方法并不是将过去所有的梯度一视同仁地相加,而是逐渐地遗忘过去的梯度, 在做加法运算时将新的梯度的信息更多地反映出来。这种操作从专业上讲,称为“指数移动平均”,呈指数函数式地减小过去的梯度的尺度。

:

3.4.2 RMSProp
  • RMSProp的数学表示:
  • RMSProp的Python实现:
  • RMSProp的优缺点:
3.4.3 Adam
Adam 融合了 Momentum 和 AdaGrad 的方法,通过组合两个方法的优点,有希望实现参数空间的高效搜索。并且 Adam 会进行超参数的“偏置校正”.
  • Adam的数学表示:
  • Adam的Python实现:
  • Adam的优缺点

正则化

1.神经网络过拟合

1.1 监督学习的目的

监督机器学习的目的是为了让建立的模型能够发现数据中普遍的一般的规律,这个普遍的一般的规律无论对于训练集合适未知的测试集,都具有较好的拟合能力。 假设空间中模型千千万,当我们站在上帝视角,心里相信总会有个最好的模型可以拟合我们的训练数据,而且这个模型不会对训练集过度学习, 它能够从训练集中尽可能的学到适用于所有潜在样本的“普遍规律”,不会将数据中噪声也学习了。这样的模型也就是我们想要的、能够有较低的泛化误差的模型.

1.2 模型过拟合

  • 过拟合含义:
    • 过拟合是指在机器学习模型训练过程中,模型对训练数据学习过度,将数据中包含的噪声和误差也学习了,使得模型在训练集上表现很好,而在测试集上表现很差的一种现象。
  • 发生过拟合的原因:
    • 训练数据少
    • 模型比较初级,无法解释复杂的数据
    • 模型拥有大量的参数(模型复杂度高)
  • 解决或者缓解过拟合的方法:
    • 获取更多的训练数据
    • 选用更好更加集成的模型
    • 为损失函数添加正则化项

1.3 监督学习正则化

  • 监督机器学习的核心原理:

    \[argmin \frac{1}{N}\sum_{i=1}^{N}L(y_i, f(x_i; \delta)) + \lambda J(f)\]

Note

  • 上述公式是机器学习中最核心、最关键、最能概述监督学习的核心思想的公式
  • 监督机器学习的核心问题
    • 确定正则化参数的同时最小化经验风险。最小化经验风险是为了让模型更加充分的拟合给定的训练数据, 而正则化参数则是控制这模型的复杂度,防止我们过分的拟合训练数据,从上面的公式可以看出, 监督机器学习的模型效果的控制有两项
  • 经验风险项
    • 经验风险项主要是由训练数据集控制,一般要求模型将最小化经验误差,为的是模型极大程度的拟合训练数据,如果该项过大则可能导致欠拟合,欠拟合好办,继续训练就是了.
    • 至少80%的单一机器学习模型都是上面这个公式可以解释的。无非就是对这两项变着法儿换样子而已。对于第一项的损失函数:
      • 如果形式是平方损失(square loss),就是线性回归
      • 如果是对数损失(log loss),就是对数几率回归
      • 如果是合页损失(hingeloss),就是支持向量机
      • 如果是指数损失(exp loss),就是 Adaboost
  • 正则化项
    • 从统计学习的角度来看,对监督机器学习加入正则化项是结构风险最小化策略的实现,正则化项一般是模型复杂度的单调递增函数,模型越复杂,正则化值就越大,所以正则化项的存在能够使得我们的模型避免走向过拟合,即防止模型过分拟合训练数据
    • 对于正则化项,\(\lambda\) 是正则化系数,通常是大于 0 的较小的正数(0.01, 0.001, …),是一种调整经验误差和正则化项之间关系的系数。所以,在实际的训练过程中,\(\lambda\) 作为一种超参数很大程度上决定了模型的好坏.
      • \(\lambda = 0\) 时相当于该公式没有正则化项,模型全力讨好第一项,将经验误差进行最小化,往往这也是最容易发生过拟合的时候.
      • 随着 \(\lambda\) 逐渐增大,正则化项在模型选择中的话语权越来越高,对模型的复杂性的惩罚也越来越厉害,模型中参数的值逐渐接近 \(0\) 或等于 \(0\).
    • 对于正则化项,正则化项的形式有很多,但常见的也就是 \(L1\)\(L2\) 正则化。也有 \(L0\) 正则化.
      • \(L0\) 正则化也就是 \(L0\) 范数,即矩阵中所有非 \(0\) 元素的个数。\(L0\) 范数就是希望要正则化的参数矩阵 \(W\) 大多数元素都为 \(0\), 简单粗暴,让参数矩阵 \(W\) 大多数元素为 \(0\) 就是实现稀疏而已.
      • \(L1\) 范数就是矩阵中各元素绝对值之和,\(L1\) 范数通常用于实现参数矩阵的稀疏性。稀疏通常是为了特征选择和易于解释方面的考虑. 在机器学习领域,\(L0\)\(L1\) 都可以实现矩阵的稀疏性,但在实践中,\(L1\) 要比 \(L0\) 具备更好的泛化求解特性而广受青睐.
      • 相较于 \(L0\)\(L1\),其实 \(L2\) 才是正则化中的天选之子。在各种防止过拟合和正则化处理过程中,\(L2\) 正则化可谓风头无二。\(L2\) 范数是指矩阵中各元素的平方和后的求根结果。采用 \(L2\) 范数进行正则化的原理在于最小化参数矩阵的每个元素,使其无限接近于 \(0\) 但又不像 \(L1\) 那样等于 \(0\), 为什么参数矩阵中每个元素变得很小就能防止过拟合?用深度神经网络来举例,在 \(L2\) 正则化中,如果正则化系数变得比较大, 参数矩阵 \(W\) 中的每个元素都在变小,线性计算的和 \(Z\) 也会变小,激活函数在此时相对呈线性状态, 这样就大大简化了深度神经网络的复杂性,因而可以防止过拟合.

1.4 常用正则化方法

  • \(L1\) 正则化, Lasso

    \[\min \frac{1}{N}\sum_{i=1}^{N}L(y_i, f(x_i)) + \lambda ||w||\]
  • \(L2\) 正则化, 岭回归(Ridge Regression)

    \[\min \frac{1}{N}\sum_{i=1}^{N}L(y_i, f(x_i)) + \frac{\lambda}{2} ||w||^{2}\]

Note

  • 两者都是对回归损失函数加一个约束项,lasso 加的是回归系数的 \(L1\) 范数,岭回归加的是回归系数 \(L2\) 范数.

2.神经网络正则化

神经网络的正则化方法:

  • 权重衰减 - \(L_2\) 正则化 - \(L_1\) 正则化 - \(L_\infty\) 正则化
  • Dropout

2.1 权值衰减(L1,L2 正则化)

  • 未正则化的交叉熵损失函数

    \[J = -\frac{1}{m}\sum_{i=1}^{m}\Big(y^{(i)}\log(\hat{y}^{(L)(i)}) + (1 - y^{(i)})\log(1 - \hat{y}^{(L)(i)})\Big)\]
  • L1 正则化的交叉熵损失函数

    \[J = \underbrace{-\frac{1}{m}\sum_{i=1}^{m}\Big(y^{(i)}\log(\hat{y}^{(L)(i)}) + (1 - y^{(i)})\log(1 - \hat{y}^{(L)(i)})\Big)}\_{\text{cross-entropy cost}} + \underbrace{\frac{1}{m}\lambda\sum_{l}\sum_{k}\sum_{j} ||W_{k,j}^{[L]}||}\_{\text{L1 regularization cost}}\]
  • L2 正则化的交叉熵损失函数

    \[J = \underbrace{-\frac{1}{m}\sum_{i=1}^{m}\Big(y^{(i)}\log(\hat{y}^{(L)(i)}) + (1 - y^{(i)})\log(1 - \hat{y}^{(L)(i)})\Big)}\_{\text{cross-entropy cost}} + \underbrace{\frac{1}{m}\frac{\lambda}{2}\sum_{l}\sum_{k}\sum_{j} W_{k,j}^{[L]2}}\_{\text{L2 regularization cost}}\]

2.2 Dropout

  • 当网络的模型变得很复杂时,权值衰减方法不能有效地对过拟合进行抑制;
  • Dropout在神经网络学习的过程中随机删除神经元:
  • 训练时,随机选出隐藏层的神经元,然后将其删除,被删除的神经元不再进行信号的传递
  • 训练时,每传递一次数据,就会随机选择要删除的神经元
  • 测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出

Dropout 是指在神经网络训练的过程中,对所有神经元按照一定的概率进行消除的处理方式。在训练深度神经网络时,Dropout 能够在很大程度上简化神经网络结构,防止神经网络过拟合。所以,从本质上而言,Dropout 也是一种神经网络的正则化方法。

假设我们要训练了一个多隐藏层的神经网络,该神经网络存在着过拟合。于是我们决定使用 Dropout 方法来处理, Dropout 为该网络每一层的神经元设定一个失活(drop)概率,在神经网络训练过程中,我们会丢弃一些神经元节点, 在网络图上则表示为该神经元节点的进出连线被删除。最后我们会得到一个神经元更少、模型相对简单的神经网络, 这样一来原先的过拟合情况就会大大的得到缓解。这样说似乎并没有将 Dropout 正则化原理解释清楚.

为什么 Dropout 可以可以通过正则化发挥防止过拟合的功能?

  • 因为 Dropout 可以随时随机的丢弃任何一个神经元,神经网络的训练结果不会依赖于任何一个输入特征,每一个神经元都以这种方式进行传播, 并为神经元的所有输入增加一点权重,Dropout 通过传播所有权重产生类似于 L2 正则化收缩权重的平方范数的效果,这样的权重压缩类似于 L2 正则化的权值衰减,这种外层的正则化起到了防止过拟合的作用。

所以说,总体而言,Dropout 的功能类似于 L2 正则化,但又有所区别。另外需要注意的一点是,对于一个多层的神经网络, 我们的 Dropout 某层神经元的概率并不是一刀切的。对于不同神经元个数的神经网络层,我们可以设置不同的失活或者保留概率, 对于含有较多权值的层,我们可以选择设置较大的失活概率(即较小的保留概率)。所以,总结来说就是如果你担心某些层所含神经 元较多或者比其他层更容易发生过拟合,我们可以将该层的失活概率设置的更高一些。

2.3 数据增强

2.4 提前终止

2.5

参数初始化

1.参数初始化

  • 在神经网络的学习中,权重 \(W\) 的的初始值特别重要。设定什么样的权重初始值,经常关系到神经网络的学习能否成功;
  • 不能将权重初始值全部设为0:
  • 因为在误差反向传播中,所有权重都会进行相同的更新;
    • 比如:在2层神经网络中,假设第1层和第2层的权重为0,这样一来,正向传播时,因为输入层的权重是0,所有第2层的权重神经元全部会被传递相同的值。第2层的神经元中全部输入相同的值,这意味着反向传播时第2层的权重全部都会进行姓童的更新,因此,权重被更新为相同的值,并拥有了对称的值,这使得神经网络拥有许多不同的权重的意义就丧失了。
  • 为了防止“权重均一化”,必须随机生成初始值;
  • 梯度消失(gradient vanishing)
  • 当使用sigmoid函数作为激活函数时,激活层的激活值呈偏向0和1的分布,随着输出不断靠近0或1,它导数的值逐渐接近0,因此,偏向0和1的数据分布会造成反向传播中梯度的值不断减小,最后消失。层次加深的深度学习中,梯度消失的问题更加严重;
  • 表现力受限
  • 如果有多个神经元都输出几乎相同的值,那他们就没有存在的意义了。比如,如果100个神经元都输出几乎相同的值,那么也可以由1个神经元来表示基本相同的事情。因此,激活值在分布上有所偏向;
  • 各层的激活函值的分布都要求有适当地广度。
  • 因为通过在各层间传递多样性的数据,神经网络可以进行高效的学习,反之,如果传递的是有所偏向的数据,就会出现梯度消失或者“表现力受限”的问题,导致学习无法顺利进行;

Xavier初始值:

在Xavier Glorot等人的论文中,推荐了权重初始值,俗称“Xavier初始值”。在一般的深度学习框架中,Xavier初始值已经被作为标准使用;

Xavier的论文中,为了使各层的激活值呈现出具有相同广度的分布,推导了合适的权重尺度。推导出的结论是:

:math:`如果前一层的节点数为 n,则初始值使用标准差为 frac{1}{sqrt{n}} 的分布;`

He初始值

Xavier初始值是以激活函数是线性函数为前提推导出来的,因为sigmoid函数和tanh函数左右对称,且中央附近可以视作线性函数,所以适合使用Xavier初始值; 当激活函数使用ReLU函数时,一般推荐ReLU专用的初始值,Kaiming He等人推荐了一种初始值,俗称“He初始值”;

\(当如果前一层的节点数为 n,则初始值使用标准差为\sqrt{\frac{2}{n}}的高斯分布;\)

结论:

  • 当激活函数使用ReLU时,权重初始值使用He初始值
  • 当激活函数为sigmoid或tanh等S型函数时,初始值使用Xavier初始值

2.Batch Normalization

  • 设定合适的权重初始值,各层的激活值分布就会有适当地广度,从而可以顺利地进行学习;
  • 为了使各层拥有适当的广度,Batch Normalization 方法“强制性”地调整激活值的分布;

2.1 Batch Normalization原理

  • Batch Normalization 的思路是调整各层的激活值分布使其拥有适当的广度。为此,要向神经网络中插入对数据分布进行的正规化层,即Batch Normalization层。

Batch Normalization,顾名思义,以进行学习时的mini-batch为单位,按mini-batch进行正规化。具体来说,就是对mini-batch数据进行数据分布的均值为0,方差为1的正规化,数学表示如下:

\(\mu_B \leftarrow \frac{1}{m}\sum_{i=1}^{m}x_i\)

\(\sigma_{B}^{2} \leftarrow \frac{1}{m}\sum_{i=1}^{m}(x_i - \mu_B)^2\)

\(\hat{x_i} \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma_{B}^{2} + \epsilon}}\)

这里对 mini-batch 的 m 个输入数据的集合 \(B=\{x_1, x_2, \ldots, x_m\}\) 求均值 \(\mu_B\)和方差 \(\sigma_B^{2}\)。然后对输入数据进行均值为 0,方差为 1 的正规化。其中 \(\epsilon\) 取一个较小的值 \(10e-7\)。即将mini-batch的输入数据 \(\{x_1, x_2, \ldots, x_m\}\) 变换为均值为0,方差为1的数据 \(\{\hat{x_1}, \hat{x_2}, \ldots, \hat{x_m}\}\)。通过将这个处理插入到激活函数的前面或后面,可以减小数据分布的偏向。

接着Batch Normalization层会对正规化后的数据进行缩放和平移的变换,数学表示如下:

\(y_i \leftarrow \gamma \hat{x_i} + \beta\)

其中:

  • \(\gamma\)\(\beta\): 是参数,初始值一般设为 \(\gamma=1\), \(\beta=0\),然后通过学习整合到合适的值;

2.2 Batch Normalization优点

  • 可以使学习快速进行(可以增大学习率);
  • 不那么依赖初始值(对初始值不敏感);
  • 抑制过拟合(降低Dropout等的必要性);

3.超参数的调优

  • 神经网络中的超参数是指,各层的神经元数量,batch大小,参数更新时的学习率,权值衰减参数(正则化参数)等;
  • 不能使用测试数据评估超参数的性能
  • 调整超参数时,必须使用超参数专用的确认数据,用于调整超参数的数据一般称为验证数据(validation data);
  • 模型训练数据的使用:
  • 训练数据用于参数(权重和偏置)的学习;
  • 验证数据用于超参数的性能评估;
  • 测试数据确认泛化能力,要在最后使用(比较理想的是只用一次);

损失函数

1.损失函数

为了使用基于梯度的学习方法,必须选择一个损失函数,并且必须选择如何表示模型的输出。下面给出基于不同学习任务常用的损失函数:

任务 符号 名字 数学形式
分类      
回归      

神经网络损失函数 - 交叉熵损失函数(cross entropy):

  1. 在大多数情况下,参数模型定义了一个分布 \(p(y|x;\theta)\),并且简单地使用极大似然原理,这意味着使用 训练数据和模型预测间的交叉熵 作为损失函数
  2. 有时使用一个更简单的方法,不是预测 \(y\) 的完整概率分布,而是仅仅预测 在给定 :math:`x` 的条件下,:math:`y` 的某种统计量
  3. 用于训练神经网络的完整的损失函数通常是在基本的机器学习损失函数的基础上结合一个正则项

2.均方误差损失函数(Mean Squared Error, MSE)

\(p_{model}(y|x) = N(y;f(x;\theta), I)\):

\[J(\theta)=\frac{1}{2}E_{x,y \sim \hat{p}_{data}} ||y-f(x;\theta)||^{2} + const\]
\[E=\frac{1}{2} \sum_{k}(y_k-t_k)^2\]

其中:

  • \(y_k\): 神经网路的输出
  • \(t_k\): 正确的目标变量
  • \(k\): 数据的维数
def mean_squared_error(y, t):
   error = 0.5 * np.sum((y - t) ** 2)

   return error

3.交叉熵误差损失函数(Cross Entropy Error, CEE)

\[L = -\frac{1}{n}\sum_{i=0}^{n}(y_i\log a_i + (1-y_i \log (1-a_i)))\]
\[E=-\sum_{k}t_k\log y_k\]

其中:

  • \(y_k\): 神经网路的输出
  • \(t_k\): 正确的解标签, {0, 1}
  • \(k\): 数据的维数
def cross_entropy_error(y, t):
   delta = 1e-7
   error = - np.sum(t * np.log(y + delta))

   return error

4.极大似然学习条件分布

大多数现代的神经网络使用极大似然来训练,这意味着损失函数就是负的对数似然,它与训练数据和模型分布间的交叉熵等价,这个损失函数表示为:

\[J(\theta)=E_{x,y \sim \hat{p}_{data}} \log p_{model}(y|x)\]

损失函数的具体形式随着模型而改变,取决于 \(\log p_{model}\) 的形式。

使用极大似然来导出损失函数的方法的一个优势是,它减轻了为每个模型设计损失函数的负担。明确一个模型 \(p(y|x)\) 则自动地确定了一个损失函数 \(\log p(y|x)\)

贯穿神经网络设计的一个反复出现的主题是损失函数的梯度必须足够的大和具有足够的预测性,来为学习算法提供一个好的指引。 饱和(变得非常平)的的函数破坏了这一目标,因为它们把梯度变得非常小,这在很多情况下都会发生, 因为用于产生隐藏单元或者输出单元的输出激活函数会饱和。负的对数似然帮助在很多模型中避免这个问题。

对于交叉熵损失函数的优化,通常采用基于 梯度下降 的算法框架对其进行优化迭代求解。这其中除了原始的梯度下降法之外, 根据一次优化所需要的样本量的不同又可分为 随机梯度下降小批量(mini-batch)梯度下降。 之后又引入了 带有历史梯度加权的带动量(momentum)的梯度下降法Rmsprop 以及声名远扬的 Adam 算法 等等:

  • 梯度下降(Gradient Descent)/
  • 随机梯度下降 (Stoictic Gradient Descent)
  • 小批量梯度下降(mini-batch Gradient Descent)
  • 带有历史梯度加权的带动量的梯度下降法 ()
  • Rmsprop
  • Adam

5.学习条件统计量

6.mini-batch学习

  • 机器学习使用训练数据进行学习,严格来说就是针对训练数据计算损失函数的值,找出使该损失函数的值最小的参数。因此,计算损失函数时必须将所有的训练数据作为对象。
  • 针对所有训练数据的损失函数:
  • 均方误差:\(E = \frac{1}{2}\sum_n \sum_k (y_{nk} - t_{nk})^2\) \(n\)为训练数据的个数
  • 交叉熵:\(E = -\frac{1}{n}\sum_n \sum_k t_{nk}\log y_{nk}\), \(n\)为训练数据的个数
  • 通过对所有训练数据的损失函数除以 \(n\),可以求得单个数据的“平均损失函数”,通过这样的平均化,可以获得和训练数据的数量无关的统一指标;
  • 在训练数据数量比较大时,如果以全部数据为对象求损失函数,计算过程需要花费较长的时间,且对计算机空间的要求也会比较高。
  • 从全部数据中选出一部分,作为全部数据的“近似”,对小部分数据进行学习,叫做mini-batch学习

7.mini-batch交叉熵

def cross_entropy_error(y, t):
   if y.ndim == 1:
      t = t.reshape(1, t.size)
      y = y.reshape(1, y.size)

   batch_size = y.shape[0]
   return -np.sum(t * np.log(y + 1e-7)) / batch_size

当监督数据是标签形式时:

def cross_entropy_error(y, t):
   if y.ndim == 1:
      t = t.reshape(1, t.size)
      y = y.reshape(1, y.size)

   batch_size = y.shape[0]
   return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

评价指标

1.常用评价指标

2.评价指标的使用

CNN

1.CNN 发展历史

CNN 在计算机视觉的三大领域:图像识别目标检测语义分割(图像分割) 有着广泛的应用.

  • 1985年,Rumelhart 和 Hinton 等人提出了 BP神经网络 (Learning internal representations by error propagation, 1985), 即著名的反向传播算法训练神经网络模型,奠定了神经网络的理论基础.
  • 深度学习三巨头(Yann LeCun, Geoffrey Hinton, Yoshua Bengio)之一 Yann LeCun 在 BP 神经网络提出之后三年, 发现可以用 BP 算法训练一种构造出来的多层卷积网络结构,并用其训练出来的卷积网络识别手写数字。 在 (Backpropagation applied to handwritten zip code recognition,1989) 一文中, LeCun 正式提出了 卷积神经网络(Convolutional Neural Network,CNN) 的概念.
  • LeCun 正式提出 CNN 概念之后,在 1998年 提出了 CNN 的开山之作 —— LeNet-5 网络 (Gradient-based learning applied to document recognition,1998).
  • 进入21世纪后,由于计算能力和可解释性等多方面的原因,神经网络的发展经历了短暂的低谷,直到 2012 年 ILSVRC 大赛上 AlexNet 一举夺魁, 此后大数据兴起,以 CNN 为代表的深度学习方法逐渐成为计算机视觉、语音识别和自然语言处理等领域的主流方法,CNN 才开始正式发展起来。

2. CNN 结构、原理

2.1 CNN 整体结构

  • 全连接的神经网络或 DNN 中,Affine 层后面跟着激活函数 ReLU 层。最后是 Affine 层加 Softmax 层输出最终结果(概率)

    • 全连接层存在的问题:
      • 因为图像是3维的,这个形状应该含有重要的空间信息。比如,空间上相邻的像素为相似的值、RGB 的各个通道之间分别有密切 的关联性、相距较远的像素之间没有什么关联等
      • 全连接层会忽略数据的形状,它会将输入数据作为相同的神经元(同一维度的神经元)处理,所以无法利用与形状相关的信息
  • 一个典型的 CNN 通常包括下面这三层:

    • 卷积层(Convolutional layer)

      • CNN 相较于 DNN,其主要区别就在于 卷积层(Convolutional layer), 卷积层的存在使得神经网络具备更强的学习和特征提取能力
      • 卷积层可以保持数据形状不变,当输入数据是图像时,卷积层会以 3 维数据的形式接收输入数据, 并同样以3维数据的形式输出至下一层。因此,在 CNN 中,可以正确理解图像等具有形状的数据
    • 池化层(Pooling layer)

      • CNN 中在卷积层后面会跟着一个 池化层(Pooling layer),池化层的存在使得 CNN 的稳健性更好
    • 全连接层(Full Connected layer)

      • CNN 的最后是 DNN 中常见的 全连接层(Full Connected layer)

2.2 卷积层(Convolutional layer)

2.2.1 卷积的数学解释
2.2.1.1 卷积的本质
  • 数学解释

    在泛函分析中,卷积也叫做 旋积 或者 褶积,是一种通过两个函数 \(x(t)\)\(h(t)\) 生成的数学算子, 其计算公式如下:

    • 连续形式

      \[x(t)h(t)(\tau)=\int_{- \infty}^{+ \infty} x(\tau)h(\tau - t)dt\]
    • 离散形式

      \[x(x)h(x)(\tau)=\sum_{\tau=-\infty}^{+ \infty} x(\tau)h(\tau - t)\]

    两个函数的卷积就是:

      • 将一个函数 \(h(t)\) 进行翻转(Reverse),然后再做一个平移(Shift),得到 \(h(t-\tau)\)
      • 将平移后的函数 \(h(x-\tau)\) 与另外一个函数 \(x(\tau)\) 对应元素相乘求和/积分
  • 图形解释

    • 函数 \(x(t)\)\(h(t)\) 图像如下:

      _images/conv_fun.png
      • 对函数 \(h(t)\) 进行翻转(Reverse),得到 \(h(-\tau)\):

        _images/h_reverse.png
      • 对函数 \(h(-\tau)\) 进行平移(Shift),得到 \(h(t-\tau)\) :

        _images/h_shift.png
    • _images/conv_sum.png
2.2.1.2 常规卷积
  • 单通道图像(灰度图)
    • 单通道卷积
    _images/conv.gif
  • 多通道图像(RGB 图)
    • 多通道卷积
    _images/n_conv.gif _images/n_conv2.gif
2.2.1.3 3D 卷积
2.2.1.4 转置卷积
  • 转置卷积(Transposed Convolution)也叫解卷积(Deconvolution)、反卷积。

    • 在常规卷积时,每次得到的卷积特征图尺寸是越来越小的,但在图像分割等领域, 需要逐步恢复输入时的尺寸,如果把常规卷积时的特征图不断变小叫做 下采样, 那么通过装置卷积来恢复分辨率的操作可以称作 上采样
    _images/normal_conv.gif _images/transposed_conv.gif
2.2.1.5 1x1 卷积
2.2.1.6 深度可分离卷积
2.2.1.7 空洞卷积
  • 空洞卷积

    • 空洞卷积也叫扩张卷积、膨胀卷积,简单来说就是在卷积核元素之间加入一些空格(零)来扩大卷积核的的过程, 可以用一个扩张率 \(a\) 来表示卷积核扩张的程度。

    • \(a=1, 2, 4\) 的时候卷积核的感受野。

      _images/dilated_conv.png
    • 扩展率 \(a=2\) 时的卷积过程

      _images/dilated_conv.gif
    • 加入空洞之后的实际卷积核尺寸与原始卷积尺寸之间的关系如下:

      \[K = k+(k-1)(a-1)\]
      • 其中:

        • \(K\) :加入空洞之后的卷积核尺寸
        • \(k\) :原始卷积核尺寸
        • \(a\): :卷积扩张率
  • 空洞卷积优点

    • 一个直接的作用就是可以扩大卷积感受野,空洞卷积几乎可以在零成本的情况下就可以获得更大的感受野来扩充更多信息,这有助于检测和分割任务中提高准确率。
    • 另一个优点就是可以捕捉多尺度的上下文信息,当使用不同的扩展率来进行卷积核叠加时,获取的感受野就丰富多样
2.2.2 CNN 卷积定义
  • 从数学的角度解释,卷积可以理解为一种类似于 加权运算 一样的操作。在图像处理中,针对图像的像素矩阵, 卷积操作就是用一个卷积核来逐行逐列的扫描像素矩阵,并与像素矩阵做元素相乘,以此得到新的像素矩阵, 这个过程称为 卷积(Convolutional). 其中
    • 卷积核也叫作 过滤器 或者 滤波器(filter)
    • 滤波器在输入的像素矩阵上扫过的面积称为 感受野()
    • 卷积层的输入输出数据称为 特征图(feature map)
      • 卷积层的输入数据称为 输入特征图
      • 卷积层的输出数据称为 输出特征图
2.2.3 CNN 卷积计算
_images/conv.gif
  • 在上面的图中,用一个 \(3 \times 3\) 的滤波器扫描一个 \(5 \times 5\) 的输入像素矩阵。用滤波器中每一个元素与像素矩阵中感受野内的元素进行乘积运算,可以得到一个 \(3 \times 3\) 的输出像素矩阵。这个输出的 \(3 \times 3\) 的输出像素矩阵能够较大程度地提取原始像素矩阵的图像特征,这也是卷积神经网络之所以有效的原因
  • 将各个位置上滤波器的元素和输入像素矩阵的对应元素相乘,然后再求和。最后,将这个结果保存到输出的对应位置; 将这个过程在输入像素矩阵的所有位置都进行一遍,就可以得到卷积运算的输出
2.2.4 CNN 卷积步幅
  • 应用滤波器的位置的间隔称为 步幅(Stirde)
  • 滤波器移动的步幅为 1 时,即滤波器在像素矩阵上一格一格平移, 滤波器也可以以 2 个或更多的单位平移
2.2.5 CNN 卷积输出维度
  • 如何确定经过卷积后的输出矩阵的维度?

  • 假设原始输入像素矩阵中: \(shape = (n, n)\),滤波器: \(shape = (f, f)\),步幅: \(s\)

    • 如果滤波器的步幅 \(s=1\),那么输出像素矩阵:

      • \(shape = (n-f+1, n-f+1)\)
    • 如果滤波器的步幅 \(s \geq 1\),那么输出像素矩阵:

      • \(shape = \Big(\frac{(n-f)}{s+1}, \frac{(n-f)}{s+1}\Big)\)
2.2.6 CNN 卷积填充
  • 问题:

    • 在进行卷积运算时,原始输入像素矩阵的边缘和角落位置的像素点只能被滤波器扫描到一次, 而靠近像素中心点位置的像素点则会被多次扫描到进行卷积,这使得边缘和角落里的像素特征提取不足;
    • 在 CNN 中进行卷积运算,输出数据的大小会越来越小,反复进行多次卷积运算后,某个时刻的输出大小就有可能变为 \(1 \times 1\),导致无法再应用卷积运算,为了避免这样的情况,就需要使用卷积填充(Padding), 可以使得卷积运算在保持空间大小不变的情况下降数据传给下一层
  • Padding 定义:

    • 在进行卷积层处理之前,需要向输入数据的周围填充固定的数据(比如0),这称为 填充(Padding)
  • Padding 作用:

    • 使用 Padding 的主要目的是为了调整输入数据的大小
      • 使得输入像素矩阵中边缘和角落的像素特征可以被充分提取
      • 使得卷积运算后的输出数据大小保持合适
  • Padding 方法:

    • valid Padding
      • 不填充
    • same Padding
      • 填充后输入和输出大小是一致的;
      • 对于 \(n \times n\) 的输入像素矩阵,如果对输入像素矩阵每个边缘填充 \(p\) 个像素, \(n\) 就变成了 \(n + 2p\),最后的输出像素矩阵的形状就变成了 \(shape = ((n+2p -f)/s+ 1, (n+2p -f)/s+ 1)\)
      • 如果想让输入数据的大小与输出数据的大小相等,即 \(n+2p-f + 1 = n\),则对输入像素矩阵每个边缘 Padding 的像素个数为 \(p = (f-1)/2\)
      • 综上,一般而言,滤波器的大小 \(f\) 都会选择为奇数个;
  • Padding 实现:

    import numpy as np
    
    def zero_pad(X, pad):
       X_pad = np.pad(X, ((0, 0), (pad, pad), (pad, pad), (0, 0)), "constant")
       return X_pad
    
    np.random.seed(1)
    x = np.random.randn(4, 3, 3, 2)
    x_pad = zero_pad(x, 2)
    fig, ax = plt.subplots(1, 2)
    ax[0].set_title("x")
    ax[0].imshow(x[0, :, :, 0])
    ax[1].set_title("x_pad")
    ax[1].imshow(x_pad[0, :, :, 0])
    
2.2.7 CNN 卷积滤波器初始化和学习
  • 在DNN中,参数有权重和偏置,在CNN中,滤波器的参数就对应DNN中的权重,并且,CNN中也存在偏置,通常是一个标量数字;
  • 在训练CNN时,需要初始化滤波器中的卷积参数,在训练中不断迭代得到最好的滤波器参数;
  • 卷积层的参数通常在于滤波器,根据滤波器的带下,可以计算一个滤波器的参数数量为 \(f * f * nc\),其中 \(nc\) 是通道数量
2.2.8 CNN 三维卷积运算
_images/3Dconv1.png _images/3Dconv2.png _images/3Dconv3.png
  • 3 维卷积运算的输入图像数据为 3 通道(channel)的 RGB 数据
    • 2 维图像数据的卷积运算都是以高、长方向的 2 维形状为对象的,通道数为 1。
      • 2 维图像数据形状为 (height, width)
    • 3 维图像数据除了高、长方向外还需要处理通道(channel)方向,通道数为 3。
      • 3 维图像数据形状为 (channel, height, width)
  • 3 维图像数据卷积运算
    • 3维数据在通道方向上特征图增加了,通道方向上有多个特征图时,会按照通道进行输入图像数据和滤波器的卷积运算,并将结果相加,从而得到输出特征图
    • 输入图像数据和滤波器的通道数要设为相同的值,并且每个通道上的滤波器形状应相同
      • 输入图像形状:(channel, input_height, input_width)
      • 滤波器:(channel, filter_height, filter_width)
    • 3 维图像数据卷积运算输出数据形状
      • 假设原始输入像素矩阵中:\(shape = (3, n, n)\),滤波器:\(shape = (3, f, f)\),步幅:\(s\),使用 same padding: 填充 \(p\) 个像素
        • 输出像素矩阵:\(shape = \Big(1, \frac{(n + 2p - f)}{s + 1}, \frac{(n + 2p - f)}{s + 1}\Big)\)
    • 3 维卷积运算对于比较复杂的图像数据进行特征提取时,可以使用多个滤波器(filter)
    • 3 维卷积层的参数数量
      • 一个滤波器的参数数量为 \(f * f * nc\),其中 \(nc\) 是通道数量, \(k\) 个滤波器的参数数量为 \(f * f * nc * k\)

2.3 池化层(Pooling layer)

2.3.1 池化层介绍
  • 通常在设计CNN时,卷积层后会跟着一个池化层;
  • 池化层的操作类似于卷积,只是将 滤波器与感受野之间的元素相乘 改成了 利用树池对感受野直接进行采样(最大/平均)
  • 池化层的参数:
    • 滤波器的大小 \(f\)
    • 步幅 \(s\)
  • 池化层只是计算神经网路某一层的静态属性,中间没有学习过程;
2.3.2 池化层的作用
  • 缩减模型大小,对输入矩阵的高度和宽度进行缩小;
  • 提高模型计算速度;
  • 提高所提取特征的稳健性;
2.3.3 池化层操作
  • 最大池化(max pooling)
    • 设置一个树池:
      • \(f \times f\)的滤波器
      • 步幅:\(s\)
    • 将输入矩阵拆分为不同的区域
    • 输出输入矩阵不同区域的最大元素值
  • 平均池化(average pooling)
    • 设置一个树池:
      • \(f \times f\)的滤波器
      • 步幅:\(s\)
    • 将输入矩阵拆分为不同的区域
    • 输出输入矩阵不同区域的元素值的平均值

2.4 全连接层(Full Connected layer)

  • 池化完成之后就是标准 DNN 中的全连接层了
  • 相邻层的所有神经元之间都有连接,这称为“全连接层(Full Connected layer)”,可以使用 Affine 层实现全连接层

3 CNN 图像学习过程

CNN的直观理解:

  • 从可视化的角度观察 CNN 每一层在图像识别过程中到底都学到了什么. 2014 年 Zeiler 等人在 ECCV 上发表了一篇基于可视化角度理解 CNN

    的经典论文,可谓是卷积神经网络可视化的开山之作(Visualizing and Understanding Convolutional Networks,2014)

  • CNN在学习过程中是 逐层对图像特征进行识别和检验 的,CNN 的不同层负责检测输入图像的不同层级的图像特征。在 CNN 中

    • 前几层网络用于检测 图像的边缘特征,包括图像的基本轮廓

      • 边缘检测的目的就是检测出图像中亮度变化和特征较为明显的点和线
    • 中间网络层用于检测 图像中物体的部分区域

    • 后几层网络用于检测 图像中完整的物体

CNN在计算机视觉领域的三大应用任务:

  • 图像分类

    • 分类:回答一张图像中是什么的问题
  • 目标检测

    • 分类+定位:不仅需要回答图像中有什么,而且还得给出这些物体在图像中的位置

      • 无人驾驶
      • 工业产品瑕疵检测
      • 医学肺部节点检测
  • 图像分割

    • 像素级的图像分割

      • 语义分割
      • 实例分割

CNN-图像分类

1.CNN 图像分类 —— 从 LeNet5 到 ResNet

2.LeNet5

  • LeNet5 的提出:

    在神经网络的和深度学习领域,Yann LeCun 在 1998 年在 IEEE 上发表了 (Gradient-based learning applied to document recognition,1998), 文中首次提出了 卷积-池化-全连接 的神经网络结构,由 LeCun 提出的七层网络命名为 LeNet-5,因而也为他赢得了 CNN 之父的美誉.

  • LeNet-5 的网络结构:

    _images/LeNet-5.png

    LeNet-5 共有5层(输入输出层不计入层数, 池化层与卷积层算1层). 每层都有一定的训练参数,其中三个卷积层的训练参数较多,每层都有多个滤波器(特征图),每个滤波器都对上一层的输出提取不同的像素特征:

    \[输入 \rightarrow (卷积-池化) \rightarrow (卷积-池化) \rightarrow 卷积(全连接) \rightarrow 全连接 \rightarrow 全连接 \rightarrow 输出\]

Note

作为标准的卷积网络结构,LeNet-5 对后世的影响深远,以至于在 16 年后,谷歌提出 Inception 网络时也将其命名为 GoogLeNet, 以致敬 Yann LeCun 对卷积神经网络发展的贡献。然而 LeNet-5 提出后的十几年里,由于神经网络的可解释性问题和计算资源的限制, 神经网络的发展一直处于低谷.

3.AlexNet

  • AlexNet 的提出:

    2012年,深度学习三巨头之一的 Geoffrey Hinton 的学生 Alex Krizhevsky 率先提出了 AlexNet (ImageNet Classification with Deep Convolutional Neural Networks,2012), 并在当年度的 ILSVRC(ImageNet大规模视觉挑战赛)以显著的优势获得当届冠军,top-5 的错误率降至了 16.4%, 相比于第二名 26.2% 的错误率有了极大的提升。这一成绩引起了学界和业界的极大关注, 计算机视觉也开始逐渐进入深度学习主导的时代.

  • AlexNet 的网络结构:

    AlexNet 共有 8 层(输入输出层不计入层数, 池化层与卷积层算1层):

    \[输入 \rightarrow (卷积-池化) \rightarrow (卷积-池化) \rightarrow 卷积 \rightarrow 卷积 \rightarrow (卷积-池化) \rightarrow 全连接 \rightarrow 全连接 \rightarrow 全连接 \rightarrow 输出\]

    AlexNet 继承了 LeNet-5 的思想,将卷积神经网络发展到很宽很深的网络中,相较 LeNet-5 的 6 万个参数, AlexNet 包含了 6 亿 3 千万条连接,6 千万个参数和 65 万个神经元.

4.ZFNet

在 2013 年的 ILSVRC 大赛中,Zeiler 和 Fergus 在 AlexNet 的基础上对其进行了微调提出了 ZFNet, 使得 top5 的错误率下降到 11.2%,夺得当年的第一.

5.VGG-Net(VGG16, VGG19)

到了 2014 年,不断的积累实践和日益强大的计算能力使得研究人员敢于将神经网络的结构推向更深层。 在 2014 年提出的 VGG-Net (Very Deep Convolutional Networks for Large-Scale Image Recognition,2014 )中, 首次将卷积网络结构拓展至 16 和 19 层,也就是著名的 VGG16 和 VGG19。

相较于此前的 LeNet-5 和 AlexNet 的 \(5 \times 5\) 卷积和 \(11 \times 11\) 卷积,VGGNet 结构中大量使用 \(3 \times 3\) 的卷积核和 \(2 \times 2\) 的池化核。

VGGNet 的网络虽然开始加深但其结构并不复杂,但作者的实践却证明了卷积网络深度的重要性。 深度卷积网络能够提取图像低层次、中层次和高层次的特征,因而网络结构需要的一定的深度来提取图像不同层次的特征.

在论文中,作者使用了 A-E 五个不同深度水平的卷积网络进行试验,从A到E网络深度不断加深,网络的具体信息如下:

_images/VGG-Net2.PNG

VGG 的网络结构非常规整,2-2-3-3-3的卷积结构也非常利于编程实现。卷积层的滤波器数量的变化也存在明显的规律, 由64到128再到256和512,每一次卷积都是像素成规律的减少和通道数成规律的增加。VGG16 在当年的 ILSVRC 以 32% 的 top5 错误率取得了当年大赛的第二名。这么厉害的网络为什么是第二名?因为当年有比 VGG 更厉害的网络, 也就是前文提到的致敬 LeNet-5 的 GoogLeNet.

6.GoogLeNet

GoogLeNet (Going Deeper with Convolutions,2014)在借鉴此前 1x1 卷积思想的基础上, 通过滤波器组合构建 Inception 模块,使得网络可以走向更深且表达能力更强。从 2014 年获得当届 ILSVRC 冠军的 Inception v1 到现在,光 Inception 网络就已经更新到 v4 了,而后基于 Inception 模块和其他网络结构的组合而成的网络就更多了,比如说 Inception Resnet。

通常在构建卷积结构时,我们需要考虑是使用 1x1 卷积、3x3 卷积还是 5x5 卷积及其是否需要添加池化操作。 而 GoogLeNet 的 Inception 模块就是帮你决定采用什么样的卷积结构。简单而言,Inception 模块就是分别采用了 1x1 卷积、 3x3 卷积和 5x5 卷积构建了一个卷积组合然后输出也是一个卷积组合后的输出。如下图所示:

对于 28x28x192 的像素输入,我们分别采用 1x1 卷积、3x3 卷积和 5x5 卷积以及最大池化四个滤波器对输入进行操作, 将对应的输出进行堆积,即 32+32+128+64=256,最后的输出大小为 28x28x256。所以总的而言,Inception 网络的基本思想就是不需要人为的去决定使用哪个卷积结构或者池化,而是由网络自己决定这些参数,决定有哪些滤波器组合。

构建好 Inception 模块后,将多个类似结构的Inception模块组合起来便是一个Inception 网络,如下图所示:

7.ResNet

深度卷积网络一开始面临的最主要的问题是梯度消失和梯度爆炸。那什么是梯度消失和梯度爆炸呢?所谓梯度消失, 就是在深层神经网络的训练过程中,计算得到的梯度越来越小,使得权值得不到更新的情形,这样算法也就失效了。 而梯度爆炸则是相反的情况,是指在神经网络训练过程中梯度变得越来越大,权值得到疯狂更新的情形, 这样算法得不到收敛,模型也就失效了。当然,其间通过设置 relu 和归一化激活函数层等手段使得我们很好的解决这些问题。 但当我们将网络层数加到更深时却发现训练的准确率在逐渐降低。这种并不是由过拟合造成的神经网络训练数据识 别准确率降低的现象我们称之为退化(degradation)。

何恺明等一干大佬就提出了残差网络 ResNet (Deep Residual Learning for Image Recognition,2015)。

要理解残差网络,就必须理解残差块(residual block)这个结构,因为残差块是残差网络的基本组成部分。 回忆一下我们之前学到的各种卷积网络结构(LeNet-5/AlexNet/VGG),通常结构就是卷积池化再卷积池化, 中间的卷积池化操作可以很多层。类似这样的网络结构何恺明在论文中将其称为普通网络(Plain Network), 何凯明认为普通网络解决不了退化问题,我们需要在网络结构上作出创新。

何恺明给出的创新在于给网络之间添加一个捷径(shortcuts)或者也叫跳跃连接(skip connection), 可以让捷径之间的网络能够学习一个恒等函数,使得在加深网络的情形下训练效果至少不会变差。残差块的基本结构如下:

以上残差块是一个两层的网络结构,输入 X 经过两层的加权和激活得到 F(X) 的输出,这是典型的普通卷积网络结构。 但残差块的区别在于添加了一个从输入 X 到两层网络输出单元的 shortcut,这使得输入节点的信息单元直接获得了与输出节点的信息单元通信的能力, 这时候在进行 relu 激活之前的输出就不再是 F(X) 了,而是 F(X)+X。当很多个具备类似结构的这样的残差块组建到一起时, 残差网络就顺利形成了。残差网络能够顺利训练很深层的卷积网络,其中能够很好的解决网络的退化问题。

或许你可能会问凭什么加了一条从输入到输出的捷径网络就能防止退化训练更深层的卷积网络?或是说残差网络为什么能有效? 我们将上述残差块的两层输入输出符号改为和,相应的就有:

\[ \begin{align}\begin{aligned}a^{[l+2]} = g(z^{[l+2]} + a^{[l]})\\加入的跳跃连接后就有:\end{aligned}\end{align} \]
\[a^{[l+2]} = g(W^{[l+2]}a^{[l+1]} + b^{[l+2]} + a^{[l]})\]

在网络中加入 L2 正则化进行权值衰减或者其他情形下,l+2 层的权值 W 是很容易衰减为零的,假设偏置同样为零的情形下就有 =。 深度学习的试验表明学习这个恒等式并不困难,这就意味着,在拥有跳跃连接的普通网络即使多加几层,其效果也并不逊色于加深之前的网络效果。 当然,我们的目标不是保持网络不退化,而是需要提升网络表现,当隐藏层能够学到一些有用的信息时,残差网络的效果就会提升。 所以,残差网络之所以有效是在于它能够很好的学习上述那个恒等式,而普通网络学习恒等式都很困难,残差网络在两者相较中自然胜出。

由很多个残差块组成的残差网络如下图右图所示:

ResNet 在 2015 年 ILSVRC 大赛上 top5 单模型的错误率达到了 3.57%,在其他数据集上也有着惊人的表现。

CNN-图像分割

1.test

2.test2

CNN-目标检测

1.从 R-CNN 到 yoloi

  • 目标检测:让计算机不仅能够识别出输入图像中的目标物体,还要能够给出目标物体所在图像中的位置;
  • 在深度学习正式成为计算机视觉领域的主题之前,传统的手工特征图像算法一直是目标检测的主要方法。在早期计算资源不发达的背景下,研究人员的图像特征表达方法有限,只能尽可能的设计更加多元化的检测算法来进行弥补,包括早期的 SIFT 检测算法、HOG 检测算法和后来著名的 DPM 模型等;
  • 深度学习之前的早期目标检测算法的发展历程如上图左边浅蓝色部分所示:
  • 2013 年之后,神经网络和深度学习逐渐取代了传统的图像检测算法而成为目标检测的主流方法。纵观这几年的深度学习目标检测发展历程,基于深度学习算法的一系列目标检测算法大致可以分为两大流派:
  • 两步走(two-stage)算法:先产生候选区域然后再进行CNN分类(RCNN系列);
  • 一步走(one-stage)算法:直接对输入图像应用算法并输出类别和相应的定为(yolo系列);

2.两部走(two-stage)算法系列

2.1 R-CNN

R-CNN 作为将深度学习引入目标检测算法的开山之作,在目标检测算法发展历史上具有重大意义。R-CNN 算法是两步走方法的代表, 即先生成候选区域(region proposal),然后再利用 CNN 进行识别分类。由于候选框对于算法的成败起着关键作用, 所以该方法就以 Region 开头首字母 R 加 CNN 进行命名。

相较于传统的滑动卷积窗口来判断目标的可能区域,R-CNN 采用 selective search 的方法来预先提取一些较可能是目标物体的候选区域, 速度大大提升,计算成本也显著缩小。总体而言,R-CNN 方法分为四个步骤:

  • 生成候选区域
  • 对候选区域使用CNN进行特征提取
  • 将提取的特征送入SVM分类器
  • 最后使用回归器对目标位置进行修正

虽然 R-CNN 在 2013年的当时可谓横空出世,但也存在许多缺陷:selective search 方法生成训练网络的正负样本候选区域在速度上非常慢, 影响了算法的整体速度;CNN 需要分别对每一个生成的候选区域进行一次特征提取,存在着大量的重复运算,制约了算法性能

2.2 SPP-Net

针对 R-CNN 的问题,提出 ResNet 的何恺明大佬提出了 SPP-Net。该算法通过在网络的卷积层和全连接层之间加入空间进字体池化层(Spatial Pyramid Pooling)来对利用 CNN 进行卷积特征提取之前的候选区域进行裁剪和缩放使 CNN 的输入图像尺寸一致。

空间金字塔池化解决了输入候选区域尺寸不一致的问题,但更重要的意义在于减少了 R-CNN 中的重复计算,大大提高的算法的速度和性能。

SPP-Net 的缺点在于经过空间金字塔层的处理后,虽然 CNN 的输入尺寸一致了,但候选框的感受野因而也变得很大,使得卷积神经网络在训练时无法有效更新模型权重。

2.3 Fast-CNN

针对 SPP-Net 的问题,2015年微软研究院在借鉴了 SPP-Net 的空间金字塔层的基础之上,对 R-CNN 算法进行了有效的改进。

Fast R-CNN 的结构如上图所示。Fast R-CNN 的改进之处在于设计了一种 ROI Pooling 的池化层结构,有效解决了 R-CNN 算法必须将图像区域剪裁、缩放到相同尺寸大小的操作。提出了多任务损失函数,每一个 ROI 都有两个输出向量:softmax 概率输出向量和每一类的边界框回归位置向量。

Fast R-CNN 虽然借鉴了 SPP-Net 的思想,但对于 R-CNN 的 selective search 的候选框生成方法依然没做改进,这使得 Fast R-CNN 依然有较大的提升空间。

2.4 Faster-CNN

2.5 Mask R-CNN

2.6 SPP-Net

3.一步走(one-stage)算法系列

纵然两步走的目标检测算法在不断进化,检测准确率也越来越高,但两步走始终存在的速度的瓶颈。在一些实时的目标检测需求的场景中,R-CNN 系列算法终归是有所欠缺。因而一步走(one-stage)算法便应运而生了,其中以 yolo 算法系列为代表,演绎了一种端到端的深度学习系统的实时目标检测效果。 yolo 算法系列的主要思想就是直接从输入图像得到目标物体的类别和具体位置,不再像 R-CNN 系列那样产生候选区域。这样做的直接效果便是快。

3.1 yolo v1

3.2 SSD

3.3 yolo v2/yolo9000

3.4 yolo v3

CNN-迁移学习

深度神经网络的训练依赖于大量高质量的打标数据,但实际研究工作中很难有这样好又多的数据供大家尝试, 而迁移学习正是为了解决这种没有数据的尴尬而产生的一种方法论;

1.test

2.test2

RNN

1.序列模型 RNN

1.1 RNN 简介

循环神经网路(Recurrent Neural Networks, RNNs) 是一种专门用来序列数据的神经网络. RNN 经常应用于 NLP, 因为它能有效地处理文本. 一般来说, RNN 有两种表现形态, 分别指时间递归和结构递归.

相比于其它神经网络只能采用固定大小的输入并产生固定大小的输出, 如 CNN. RNN 可以将长度可变的序列作为输入和输出, 以下是 RNN 的一些示例:

_images/RNN2.png
  • RNN 这种处理序列的能力非常有用, 比如(除一对一):

    • 一对一(1 vs 1)
      • CNN
    • 一对多(1 vs N)
      • 将图像转化为一行文字
      • 根据类别生成对应音乐
    • 多对一(N vs 1)
      • 文本情感分类, 测量一段话是正面还是负面的情趣
    • 多对多(N vs M)
      • seq2seq, 机器翻译, 语音识别…
      • 输入和输出之间存在时差
    • 多对多(N vs N)
      • 命名实体识别
      • 给一个录像中的每一帧贴标签
      • 输入和输出之间没有时差
  • 序列类数据类型

    • 文本(单词序列、字符序列)数据
    • 时间序列数据
    • 一般的序列数据
  • RNN 应用

    • 文档分类
    • 语音识别
    • 自然语言处理
    • 时间序列预测
  • RNN 表现形态

    • 循环
    • 递归
      • 时间递归
      • 结构递归
  • RNN 模型

    • RNN (Recurrent Neural Network) 循环神经网络
    • 1D Convnet 一维卷积神经网路
    • LSTM (Long short-term memory) 长短期记忆网络
    • GRU (Gate Recurrent Unit) 门控循环单元

Note

  • 普通神经网络和循环神经网络的区别

    _images/DNNvsRNN.png
  • 对于序列数据, 其他神经网络模型不可行的理由:

    • 输入、输出的长度是否相等以及输入大小不固定的问题.
      • 在语音识别问题中, 输入音频序列和输出文本序列很少情况下是长度相等的, 普通网络难以处理这种问题
    • 普通神经网络结构不能共享从文本不同位置上学到的特征
      • 简单来说就是如果神经网络已经从位置 1 学到了 louwill 是一个人名, 那么如果 louwill 出现在其他位置, 神经网络就可以自动识别它就是已经学习过的人名, 这种共享可以减少训练参数和提高网络效率, 普通网咯不能达到这样的目的

Note

不管是哪个方向的应用, 只要它是属于监督机器学习性质的深度学习问题, 都可以将其归纳为一个从输入到输出的有监督机器学习问题.

1.2 RNN 网络架构

  • 使用 RNN 面临的问题场景梳理:

    • 假设在进行语音识别时, 给定了一个输入音频片段 \(X\), 要求输入一个文本片段 \(Y\), 其中输入 \(X\) 是一个按照时间播放的音频片段, 输出 \(Y\) 是一个按照顺序排列的单词组成的一句话, 所以在RNN中输入、输出都是序列性质的. 针对这样的输入、输出 \((X, Y)\) 的有监督学习, 最适合的神经网络结构就是 RNN.
1.2.1 RNN 基本结构

下面是 RNN 的基本结构, 左边是一个统一的表现形式, 右边则是左边展开的图解.

_images/RNN_base.PNG

在这样的 RNN 中, 当预测 \(y_t\) 时, 不仅要使用 \(x_t\) 的信息, 还要使用 \(x_{t-1}\) 的信息, 因为在横轴路径上的隐状态激活信息 \(h_{t-1}\) 得以帮助预测 \(y_t\).

1.2.2 RNN 单元结构
  • RNN 单元结构和两次计算:

    _images/RNN_unit.PNG
    • RNN 单元结构通常需要两次计算:

      • 一次是结合上一个时间步的隐状态值 \(W_{aa} a^{<t-1>}\) 和输入 \(W_{ax}x^{<t>} + b_a\) 的计算
      • 另一次是基于当前隐状态值 \(W_{ya}a^{<t>} + b_y\) 的输出计算
    • RNN 单元结构数学表达式:

      \[\alpha^{<t>} = tanh(W_{ax}x^{<t>} + W_{aa} a^{<t-1>} + b_a)\]
      \[\hat{y}^{<t>} = softmax(W_{ya}a^{<t>} + b_y)\]

      其中:

      • 隐藏层的激活函数一般采用 \(tanh(\cdot)\)
      • 输入、输出层的激活函数一般使用 \(sigmoid(\cdot)\)\(softmax(\cdot)\) 函数
  • 多个 RNN 单元结构组合在一起就是 RNN 结构

    _images/RNN.PNG
1.2.3 RNN 变种结构模型
  • 带有时间和记忆属性的神经网路模型使得深度学习可以胜任语音识别和自然语言处理等序列建模问题. 当然, 上面介绍的 RNN 结构是最基础、最经典的网络结构, 在这个结构的基础上, RNN 针对输入输 出序列的长度异同和记忆强度有各种各样的变种模型:
class RNN:

   def step(self, x):
      # update the hidden state
      self.h = np.tanh(np.dot(self.W_hh, self.h) + np.dot(self.W_xh, x))
      # compute the output vector
      y = np.dot(self.W_hy, self.h)

      return y

2. RNN 示例

  • Sentiment Analysis task

    • 二分类问题, 使用 “N vs 1” RNN

    • dataset

      • _images/RNN_data.png
  • RNN 模型说明

    _images/RNN_many2one.png
    • \(x_{i}\): 文本中一个单词的向量表示

    • \(y\): 包含两个数值的向量, 两个数值分别代表正面、负面

      • 输出激活函数使用 \(Softmax\) 函数将输出值转换为概率, 并确定正负
  • 构建 RNN

    • 1.数据预处理

      • 1.1 数据

        # 数据
        train_data = {
           'good': True,
           'bad': False,
           # ... more data
        }
        
        test_data = {
           'this is happy': True,
           'i am good': True,
           # ... more data
        }
        
      • 1.2 构建一个数据中存在所有单词的词汇表

        from data import train_data, test_data
        
        # 建立词汇表
        vocab = list(set([w for text in train_data.keys() for w in text.split(" ")]))
        vocab_size = len(vocab)
        print("%d unique words found" % vocab_size)
        

RNN-LSTM

1.LSTM-让RNN具备更好的记忆机制

1.1 RNN 网络

_images/RNN_unit2.png _images/RNN_units.png

1.1 LSTM 网络

梯度爆炸和梯度消失对 RNN 的影响非常大, 当 RNN 加深时, 因为梯度消失的问题使得前层的网络权重得不到更新, RNN 的记忆性就很难生效. 在传统的 RNN 基础上, 研究人员给出了一些著名的改进方案, 即 RNN 变种网络, 比较著名的是 GRU(循环门控单元)和 LSTM(长短期记忆网络); GRU 和 LSTM 二者的结构基本一致, 但有些许不同.

Long Short Term Memory networks, LSTM, 是一种特殊的 RNN 网络, 能够学习序列数据中的信息的长期依赖关系。 LSTM 由 Hochreiter&Schmidhuber(1997) 提出的, 并在随后的工作中被许多人进行提炼和推广. LSTM 在传统的 RNN 结构上做了相对复杂的改进, 这些改进使得 LSTM 相对于经典的 RNN 能够很好地解决梯度爆炸和梯度消失的问题, 让 RNN 具备更好的记忆性能, 这也是 LSTM 的价值所在. LSTM 在各种各样的序列处理问题上都表现出色, 现在已被广泛使用。

LSTM 的设计明确避免了长期依赖的问题, 长时间记住信息实际上是它们的默认行为, 而不是它们努力学习的东西。 所有的 RNN 都具有神经网络的重复模块链的形式。在标准的 RNN 中, 此重复模块将具有非常简单的结构, 例如单个 tanh 层:

_images/RNN_layer.png deeplearning/RNN/../../images/RNN_unit.png

LSTM 也具有上面的这种链状结构, 但是重复模块具有不同的结构, 而不是只有一个神经网络层, 而是有四个非常特殊的方式进行交互的层:

_images/LSTM_layer.png deeplearning/RNN/../../images/LSTM_unit.png

Note

_images/LSTM_elements.png

1.2 LSTML 核心思想

1.2.1 LSTM cell state

LSTM 最关键的部分是 LSTM 单元的最上层有一条水平贯穿的关于记忆细胞(remember cell) \(c_{t-1}\)\(c_{t}\) 的箭头直线 cell state, 这个单元状态有点像传送带, 它在整个链中一直沿直线运行, 只有一些较小的线性相互作用, 信息很容易不加改动地流动, 这样贯穿的直线表现记忆信息在网络各层之间保持下去很容易:

_images/LSTM_cell_state.png

LSTM 确实具有删除信息或将信息添加到 cell state 的能力, 这些信息由称为 gate(门) 的结构调节, gate 是一种选择性地让信息通过的方式, 它们由 Sigmoid 神经网络层和逐点乘法运算组成:

_images/LSTM_gate.png

Sigmoid 层的输出数值结果是在 \([0, 1]\) 之间的数, 描述了每个组件应该允许通过多少信息, 值为 0 表示 “不让任何信息通过”, 值为 1 表示 “让所有信息通过”.

1.2.2 LSTM 分步分解
  • 记忆细胞(remember cell, cell state)

    _images/LSTM_cell_state.png _images/LSTM_cell_state_layer.png
  • 遗忘门(forget gate)

    • 遗忘门就是要决定从记忆细胞中是否丢弃某些信息, 通过一个 Sigmoid 函数进行处理, 可以看到, 遗忘门接受来自输入 \(x_{t}\) 和上一层隐状态 \(h_{t-1}\) 的值进行加权计算处理.
    _images/LSTM_forget_gate_layer.png _images/LSTM_forget_gate.png
  • 更新门/输入门(update/input gate)

    • 更新门确定什么信息能存入记忆细胞状态中, 除了计算更新门之外, 还需要通过 \(tanh\) 计算记忆细胞的候选值 \(\tilde{C}_{t}\). 然后, LSTM 结合遗忘门 \(f_{t}\)、更新门 \(i_{t} * \tilde{C}_{t}\)、 上一层记忆细胞值和记忆细胞候选值 \(C_{t-1}\) 来共同决定和更新当前细胞状态 \(C_{t}\).
    _images/LSTM_input_gate_layer.png _images/LSTM_update_gate_layer.png _images/LSTM_input_gate.png _images/LSTM_update_gate.png
  • 输出门(output gate)

    • LSTM 提供了单独的输出门
    _images/LSTM_output_gate_layer.png _images/LSTM_output_gate.png

2.LSTM 变形

2.1 LSTM + peephole connections

_images/LSTM_peepholes.png

2.2 LSTM + coupled & input gates

_images/LSTM_coupled_input.png

2.3 GRU, Gated Recurrent Unit

_images/LSTM_GRU.png

RNN-GRU

1.GRU

GRU (Gate Recurrent Unit) 是循环神经网络的一种变体。和 LSTM 一样, GRU 也是为了解 决长期记忆和反向传播中出现的梯度消失和梯度爆炸问题而提出来的。GRU 和 LSTM 在很多种情况 下实际表现相差无几, 但 GRU 更容易训练, 能够很大程度上提高训练效率。

GRU 使用了 Update gate 和 Reset gate, 这是两个向量, 用来决定哪些信息可以进入输出. 它们的特征是可以用来训练对信息进行长时间的记忆, 而不会随着时间的流逝而担心与预测有关的信息会消失。

与 LSTM 相比, GRU 内部少了一个 “门控”, 参数比 LSTM 少, 但是却也能够达到与 LSTM 相当的功能。 考虑到硬件的计算能力和时间成本, 因而很多时候我们也就会选择更加”实用“的 GRU。

1.1 GRU 结构

1.1.1 GRU 神经元

GRU 的输入输出结构与普通的 RNN 一样, 有一个 \(x^{t}\) 和 上一个节点传递下来的隐状态(hidden state) \(h^{t-1}\), 这个隐状态包含了之前节点的相关信息。结合 \(x^{t}\)\(h^{t-1}\), GRU 会得到当前隐藏节点的输出 \(y^{t}\) 和传递给下一个节点的隐状态 \(h^{t}\).

1.1.2 GRU Update gate 和 Reset gate
_images/GRU_r_z.jpg
  • Reset gate 用来…

    \[ \begin{align}\begin{aligned}r = \sigma(W^{r} (x^{t}, h^{t-1})^{T})\\\sigma(\cdot) = \frac{1}{1+e^{-x}}\end{aligned}\end{align} \]
  • Update gate 用来帮助模型决定多少上一步(时间 t-1)的信息需要传递到下一步(时间 t)

    \[ \begin{align}\begin{aligned}z = \sigma(W^{z} (x^{t}, h^{t-1})^{T})\\\sigma(\cdot) = \frac{1}{1+e^{-x}}\end{aligned}\end{align} \]
1.1.3 GRU 层次结构
_images/GRU_unit.png

Note

  • \(\odot\) 是 Hadamard Product, 操作矩阵中对应的元素相乘
  • \(\oplus\) 是矩阵加法操作

1.GRU 重置记忆

(1)在这个阶段, 使用 Reset gate \(r\) 控来得到“重置”之后的数据:

\[h^{t-1 '} = h^{t-1} \odot r\]

(2)再将 \(h^{t-1 '}\) 与输入 \(x^{t}\), 再通过一个 \(tanh\) 激活函数来将数据缩放到 \([-1, 1]\) 的范围内:

\[h' = tanh(W (x_{t}, h_{t-1}'))\]

这里的 \(h^{'}\) 主要是包含了当前输入的 \(x^{t}\) 数据。有针对性地对 \(h^{'}\) 添加到当前的隐藏状态, 相当于“记忆了当前时刻的状态”。类似于 LSTM 的选择记忆阶段

2.GRU 更新记忆

(1)在这个阶段, 使用 Update gate \(z\) 同时进行了遗忘了记忆两个步骤, GRU 很聪明的一点就在于, 使用了同一个门控 \(z\) 就同时可以进行遗忘和选择记忆(LSTM则要使用多个门控):

\[h^{t} = z \odot h^{t-1} + (1-z)\odot h^{'}\]
  • 门控信号(这里的 \(z\)) 的范围为 \([0, 1]\)。门控信号接近1, 代表“记忆”下来的数据越多, 而接近 0 则代表“遗忘”的越多

(2)更新记忆解释

  • \(z \odot h^{t-1}\):表示对原本隐藏状态的选择性“遗忘”。这里的 \(z\) 可以想象成遗忘门 (forget gate), 忘记 \(h^{t-1}\) 维度中一些不重要的信息
  • \((1-z) \odot h^{'}\):表示对包含当前节点信息的 \(h^{'}\) 进行选择性“记忆”。与上面类似, 这里的 \((1-z)\) 同理会忘记 \(h^{'}\) 维度中的一些不重要的信息。或者, 这里我们更应当看作是对 \(h^{'}\) 维度中的某些信息进行选择
  • \(h^{t}\) 的操作就是忘记传递下来的 \(h^{t-1}\) 中的某些维度信息, 并加入当前节点输入的某些维度信息
  • 遗忘 \(z\) 和选择 \((1-z)\) 是联动的。也就是说, 对于传递进来的维度信息, 我们会进行选择性遗忘, 则遗忘了多少权重 \((z)\), 我们就会使用包含当前输入的 \((h^{'})\) 中所对应的权重进行弥补 \((1-z)\), 以保持一种“恒定”状态。
  • \(h^{'}\) 实际上可以看成对应于 LSTM 中的 hidden state;上一个节点传下来的 \(h^{t-1}\) 则对应于 LSTM 中的 cell state。\(z\) 对应的则是 LSTM 中的 \(z^{f}\) forget gate, 那么 \((1-z)\) 我们似乎就可以看成是选择门 \(z^{i}\) 了.

1.2 GRU 结构2

  • GRU

    _images/GRU_rnn.png _images/GRU_unit2.png
1.2.1 Update gate
  • update gate z_t for time step t, The update gate helps the model to determine how much of the past information (from previous time steps) needs to be passed along to the future
_images/GRU_z.png
\[z_{t} = \sigma(W^{(z)} x_{t} + U^{(z)} h_{t-1})\]
1.2.2 Reset gate
  • The reset gate is used from the model to decide how much of the past information to forget
_images/GRU_r.png
\[r_{t} = \sigma(W^{(r)} x_{t} + U^{(r)} h_{t-1})\]
1.2.3 Current memery content
_images/GRU_current.png
\[h_{t}^{'} = \tanh(W \cdot x_{t} + r_{t} \odot U \cdot h_{t-1})\]
1.2.4 Final memory at current time step
_images/GRU_output.png
\[h_{t} = z_{t} \odot h_{t-1} + (1-z_{t}) \odot h_{t}^{'})\]

RNN-Attention-Augmented

1.注意力机制原理

seq2seq 模型虽然强大, 但如果仅仅是单一使用的话, 效果会大打折扣。 本节要介绍的注意力模型就是基于编码-解码框架下的一种模拟人类注意力直觉的一种模型。

2.test

RNN–语音识别

1.语音数据的表示方式

语音 通常是由 音频信号 构成的, 而音频信号本身又是以 声波 的形式传递的, 一段语音的 波形 通常是一种时序状态, 也就是说音频信号是按照时间顺序播放的。 通过一些预处理和转换技术, 可以将声波转换为更小的声音单元, 即 音频块. 所以在语音识别的深度学习模型中, 输入就是原始的语音片段经过预处理之后的一个个音频块, 这样的音频块是以序列形式存在的。

语音识别的输入是一个序列, 输出通常是以一段文字的形式呈现, 这段文字也是按顺序排列的文本序列, 所以语音识别的输出也是一个序列。说白了, 语音识别热舞就是建立 序列输入序列输出 之间的有监督机器学习模型.

RNN–The Unreasonable Effectiveness of RNN

1.RNN

GAN

5.GAN 生成式对抗网络介绍

生成式对抗网络 (Generative Adversial Networks, GAN) 自从问世以来就颇受瞩目,相对于变分自编码器 (AutoEncoder),生成式对抗网络也可以学习图像的潜在空间表征,它可以生成与真实图像在统计上几乎无法区分的合成图像.

开创 GAN 的论文是 Ian Goodfellow 的 Generative Adversarial Network.

5.1 GAN 的核心思想和基本原理

GAN 的核心思想就在于两个部分:伪造网络鉴定网络。两者互相对抗,共同演进,在此过程中大家的水平都越来越高,伪造者网络生成的图像就足以达到以假乱真的水平。基于这个思想,下面解释 GAN 的原理于细节。

GAN 的基本原理就在于两个网络:G(Generator) 和 D(Discriminator),分别是生成器判别器

  • 生成器网络以一个随机向量作为输入,并将其解码生成一张图像
  • 判别器将一张真实或者合成的图像作为输入,并预测该图像是来自于真实数据还是合成的图像

在训练过程中,生成网络 G 的目标就是尽量生成真实的图片去欺骗判别网络 D。而判别器网络 D 的目标就是尽量把 G 生成的图片和真实图片分别开来。这样,G 和 D 就构成了一个动态的“博弈过程”。

在理想状态下下,博弈的结果就是 G 可以生成足以以假乱真的图片 G(z),而此时的 D 难以判定生成的图像到底是真是假,最后得到 \(D(G(z)) = 0.5\) 的结果。这块的理解跟博弈论中零和博弈非常类似,可以说 GAN 借鉴了博弈论中相关思想和方法.

  • Random vector from the laten space
  • => Generator(decoder)
    • => Generated(decoded) image => Mix of real and fake images
  • => Discriminator
    • => “Real”,”Fake” => Training feedback
  • => Generator(decoder)

\(\min_{G}\max_{D}V(D, G) = E_{x \sim P_{data}(x)}[logD(x)] + E_{z \sim p_{z}(z)}[log(1 - D(G(z)))]\)

下图是真实数据和生成数据所代表的的两个分布在 GAN 训练中的演化过程:

GAN 在训练过程中,同时更新判别分布(D,蓝色虚线)使 D 能区分数据真实分布px(黑色虚线)中的样本和生成分布pg (G,绿色实线) 中的样本。下面的黑色箭头表示生成模型 x=G(z) 如何将分布pg作用在转换后的样本上。可以看到,在经过若干次训练之后,判别分布接近某个稳定点,此时真实分布等于生成分布,即pdata=pg。判别器将无法区分训练数据分布和生成数据分布,即D(x)=1/2

生成器和判别器的优化算法都是随机梯度下降。但有个细节需要注意:第一步我们训练D,D是希望V(G, D)越大越好,所以这里是梯度上升(ascending)。第二步训练G时,V(G, D)越小越好,所以到这里则是梯度下降(descending)。整个训练是一个动态的交替过程。

上面我们已经知道了极小化极大的二人博弈问题的全局最优结果为pg=pdata,在给定任意生成器G的情况下,考虑最优判别器D。给定任意生成器G,判别器D 的训练标准为最大化目标函数\(V(G, D)\)

可以看到,对于任意不为零的 \((a, b)\),函数 \(y=alog(y)+blog(1-y)\)\([0,1]\) 中的 \(a/a+b\) 处达到最大值.

5.2 DCGAN 深度卷积对抗网络

自从GoodFellow提出GAN以后,GAN就存在着训练困难、生成器和判别器的loss无法指示训练进程、生成样本缺乏多样性等问题。为了解决这些问题,后来的研究者不断推陈出新,以至于现在有着各种各样的GAN变体和升级网络。比如 LSGAN,WGAN,WGAN-GP,DRAGAN,CGAN,infoGAN, ACGAN,EBGAN,BEGAN,DCGAN以及最近号称史上最强图像生成网络的BigGAN等等。本节仅选取其中的DCGAN——深度卷积对抗网络进行简单讲解并利用keras进行实现,所谓 DCGAN,顾名思义就是生成器和判别器都是深度卷积神经网络的 GAN.

DCGAN 的原始论文为 UNSUPERVISED REPRESENTATION LEARNING WITH DEEP CONVOLUTIONAL GENERATIVE ADVERSARIAL NETWORKS.

搭建一个稳健的 DCGAN 的要点在于:

  • 所有的 pooling 层使用步幅卷积(判别网络)微步幅卷积(生成网络)进行替换
  • 在生成网络和判别网络上使用批处理规范化
  • 对于更深的架构移除全连接隐藏层
  • 在生成网路的所有层上使用 ReLU 激活函数,除了输出层使用 Tanh 激活函数
  • 在判别网络的所有层上使用 LeakReLU 激活函数

5.3 Keras 搭建一个 DCGAN

import os
import numpy as np
import keras
from keras.datasets import cifar10
from keras.preprocessing import image
from keras.layers import Dense, Conv2D, LeakyReLU, Dropout, Input
from keras.layers import Reshape, Conv2DTranspose, Flatten
from keras.models import Model
from keras import optimizers, losses, metrics
import warnings
warnings.filterwarnings("ignore")

# ======================================
# Parameter
# ======================================
# 潜变量维度
laten_dim = 32

# 输入像素维度
height = 32
width = 32
channels = 3

epochs = 10000
batch_size = 20
save_dir = "./image"
start = 0

# ======================================
# Data
# ======================================
(x_train, y_train), (_, _) = cifar10.load_data()
# 指定青蛙图像(编号为6)
x_train = x_train[y_train.flatten() == 6]
x_train = x_train.reshape((x_train.shape[0],) + (height, width, channels))
x_train = x_train.astype("float32")
x_train /= 255


# ======================================
# Model
# ======================================
# 搭建生成器网络
generator_input = Input(shape = (laten_dim,))
x = Dense(128 * 16 * 16)(generator_input)
x = LeakyReLU()(x)
x = Reshape((16, 16, 128))(x)
x = Conv2D(256, 5, padding = "same")(x)
x = LeakyReLU()(x)
x = Conv2DTranspose(256, 4, strides = 2, padding = "same")(x)
x = LeakyReLU()(x)
x = Conv2D(256, 5, padding = "same")(x)
x = LeakyReLU()(x)
x = Conv2D(256, 5, padding = "same")(x)
x = LeakyReLU()(x)
x = Conv2D(channels, 7, activation = "tanh", padding = "same")(x)
generator = Model(generator_input, x)
generator.summary()

# 搭建判别器网路
discriminator_input = Input(shape = (height, width, channels))
x = Conv2D(128, 3)(discriminator_input)
x = LeakyReLU()(x)
x = Conv2D(128, 4, strides = 2)(x)
x = LeakyReLU()(x)
x = Conv2D(128, 4, strides = 2)(x)
x = LeakyReLU()(x)
x = Conv2D(128, 4, strides = 2)(x)
x = LeakyReLU()(x)
x = Flatten()(x)
x = Dropout(0.4)(x)
x = Dense(1, activation = "sigmoid")(x)
discriminator = Model(discriminator_input, x)
discriminator.summary()
discriminator_optimizer = optimizers.RMSprop(lr = 0.008,
                                             clipvalue = 1.0,
                                             decay = 1e-8)
# 将判别器参数设置为不可训练
discriminator.trainable = False
gan_input = Input(shape = (laten_dim,))
gan_output = discriminator(generator(gan_input))
# 搭建对抗网络
gan = Model(gan_input, gan_output)


# ======================================
# Model compile
# ======================================
gan_optimizer = optimizers.RMSprop(lr = 0.0004,
                                   clipvalue = 1.0,
                                   decay = 1e-8)
gan.compile(optimizers = gan_optimizer,
            loss = "binary_crossentropy")


# ======================================
# Model training
# ======================================
for step in range(epochs):
    # 潜在空间随机采样, 解码生成虚拟图像
    random_laten_vectors = np.random.normal(size = (batch_size, laten_dim)) # 20, 32
    generated_images = generator.predict(random_laten_vectors)
    stop = start + batch_size                                               # 0 + 32
    real_images = x_train[start:stop]                                       # x_train[0:0+32]

    # 将虚假图像和真实图像混合
    combined_images = np.concatenate([generated_images, real_images])
    labels = np.concatenate([np.ones((batch_size, 1)), np.zeros((batch_size, 1))])

    # 向标签中添加随机噪声
    labels += 0.05 * np.random.random(labels.shape)
    # 训练判别器
    d_loss = discriminator.train_on_batch(combined_images, labels)
    # 潜在空间随机采样
    random_laten_vectors = np.random.normal(size = (batch_size, laten_dim))
    # 合并标签,以假乱真
    misleading_targets = np.zeros((batch_size, 1))
    # 通过GAN模型来训练生成器模型,冻结判别器模型权重
    a_loss = gan.train_on_batch(random_laten_vectors, misleading_targets)
    start += batch_size
    if start > len(x_train) - batch_size:
        start = 0

    # 每100步绘图并保存
    if step % 100 == 0:
        gan.save_weights("gan.h5")
        print("discriminator loss:", d_loss)
        print("adversarial loss:", a_loss)
        img = image.array_to_img(generated_images[0] * 255., scale = False)
        img.save(os.path.join(save_dir, "generated_forg" + str(step) + ".png"))
        img = image.array_to_img(real_images[0] * 255., scale = False)
        img.save(os.path.join(save_dir, "real_forg" + str(step) + ".png"))

VAE

深度生成模型

1.AutoEncoder 自编码器

作为一种无监督或者自监督模型,自编码器本质上是一种数据压缩方法。从现有情况来看,无监督学习很有可能是一把决定深度学习未来发展方向的钥匙,在缺乏高质量打标数据的监督机器学习时代,若是能在无监督学习方向上有所突破对于未来深度学习的发展意义重大。

自编码器(AutoEncoder, AE)就是一种利用反向传播算法使得输出值等于输入值的神经网络,它先将输入压缩成潜在空间表征,然后将这种表征重构为输出。所以从本质上来讲,自编码器是一种数据压缩算法,其压缩和解压缩算法都是通过神经网络来实现的。自编码器有如下三个特点:

  • 数据相关性
    • 自编码器只能压缩与自己此前训练数据类似的数据
  • 数据有损性
    • 自编码器在解压时得到的输出与原始输入相比会有信息损失,所以自编码器是一种数据有损的压缩算法
  • 自动学习性
    • 自编码器是从数据样本中自动学习的,这意味着很容易对指定类的输入训练出一种特定的编码器,而不需要完成任何新的工作

LSTM 生成文本

本节将会探讨如何将循环神经网络用于生成序列数据。将以文本生成为例,但同样的 技术也可以推广到任何类型的序列数据,可以将其应用于音符序列来生成新音乐, 也可以应用于笔画数据的时间序列,以此类推。

序列数据生成绝不仅限于艺术内容生成。它已经成功应用与语音合成和聊天机器人的对话生成。 Google 于 2016 年发布的 Smart Reply (智能回复)功能,能够对电子邮件或短信自动生成 一组快速回复,采用的也是相似的技术。

1.生成式循环网络简史

截至 2014 年年底,还没什么人见过 LSTM 这一缩写,即使在机器学习领域也不常见。 用循环网络生成序列数据的成功应用在 2016 年才开始出现在主流领域。但是,这些技术都 有着相当长的历史,最早的是 1997 年开发的 LSTM 算法。这一算法早期用于逐字符的生成文本。

  • HOCHREITER S,SCHMIDHUBER J.Long short-term memery [J]. Neural Computation, 1997, 9(8):1735-1780.

2.如何生成序列数据

  • 如何生成序列数据

    • 用深度学习生成序列数据的通用方法,就是使用前面的标记作为输入, 训练一个网络(通常是循环神经网络或卷积神经网络)来预测序列中接下来的一个或多个标记。
    • 例如,给定输入 the cat is on the ma,训练网络来预测目标 t,即下一个字符。
  • 语言模型

    • 标记(token) 通常是单词或字符,给定前面的标记,能够对下一个标记的概率进行建模的任何网络都叫做 语言模型(language model)
    • 语言模型能够捕捉到语言的 潜在空间(laten space),即语言的统计结构
    • 一旦训练好了这样一个语言模型,就可以从中采样(sample, 即生成新序列)。向模型中输入一个初始文本字符串(即条件数据(conditioning data)), 要求模型生成下一个字符或下一个单词(甚至可以同时生成多个标记),然后将生成的输出添加到输入数据中,并多次重复这一过程。 这个循环可以生成任意长度的序列,这些序列反映了模型训练数据的结构,它们与人类书写的句子几乎相同。
  • LSTM 字符级神经语言模型

    • 将会用到一个 LSTM 层,向其输入从文本语料中提取的 N 个字符组成的字符串,然后训练模型来生成第 N + 1个字符。 模型的输出是对所有可能的字符做 softmax,得到下一个字符的的概率分布。这个 LSTM 叫作字符级的神经语言模型(character-level neural language model)。

3.采样策略的重要性

生成文本时,如何选择下一个字符至关重要:

  • 贪婪采样(greedy sampling)

    • 始终选择可能性最大的下一个字符,这种方法会得到重复的、可预测的字符串,看起来不像是连贯的语言
  • 随机采样(stochastic sampling)

    • 在采样过程中引入随机性,即从下一个字符的概率分布中进行采样
    • 从模型的 softmax 输出中进行概率采样是一种很巧妙的方法,它甚至可以在某些时候采样到不常见的字符, 从而生成看起来更加有趣的句子,而且有时会得到训练数据中没有的、听起来像是真实存在的新单词,从而 表现出创造性。但这种方法有一个问题,就是它在采样的过程中无法控制随机性的大小。

Note

  • 为什么需要有一定的随机性?

    • 极端例子
      • 考虑一个极端的例子,纯随机采样,即从均匀概率分布中抽取下一个字符,其中每个字符的概率相同。 这种方案具有最大的随机性,换句话说,这种概率分布具有最大的熵。当然,它不会生成任何有趣的内容
      • 再看一个极端的例子,贪婪采样,贪婪采样也不会生成任何有趣的内容,它没有任何随机性, 即相应的概率分布具有最小的熵
    • 从真实概率分布(即模型 softmax 函数输出的分布)中进行采样,是这两个极端之间的一个中间点。 但是,还有许多其他中间点具有更大或更小的熵。
      • 更小的熵可以让生成的序列具有更加可预测的结构(因此可能看起来更真实)
      • 更大的熵会得到更加出人意料且更有创造性的序列
    • 从生成式模型中进行采样时,在生成过程中探索不同的随机性大小总是好的做法。我们人类是生成数据是否有趣的最终判断者, 所以有趣是非常主观的,无法提前知道最佳熵的位置。

为了在采样的过程中控制随机性的大小,可以引入一个叫做 softmax 温度(softmax temperature) 的参数, 用于表示采样概率分布的熵,即表示所选择的下一个字符会有多么出人意料或多么可预测。给定一个 temperature 值, 按照下列方法对原始概率分布(即模型的 softmax 输出)进行加权,计算得到一个新的概率值。

import numpy as np

def reweight_distribution(original_distribution, temperature = 0.5):
   distribution = np.log(original_distribution) / temperature
   distribution = np.exp(distribution)
   return distribution / np.sum(distribution)

4.实现字符级的 LSTM 文本生成

任务:训练一个语言模型,这个模型是针对尼采的一些已被翻译为英文的作品的写作风格和主题的模型

5.总结

  • 可以生成离散的序列数据,其方法是:给定前面的标记,训练一个模型来预测接下来的一个或多个标记
  • 对于文本来说,这种模型叫做语言模型。它可以是单词级的,也可以是字符级的
  • 对于一个标记进行采样,需要在坚持模型的判断与引入随机性之间寻找平衡
  • 处理这个问题的一种方法是使用 softmax 温度。一定要尝试多种不同的温度,以找到合适的那一个

神经风格迁移

test 1

test 1

DeepDream

test 1

test 2

GNN

1.test1

2.test2

NLP

自然语言处理是人工智能和语言学领域的分支学科。此领域探讨如何处理及运用自然语言, 自然语言处理包括多方面和步骤,基本有认知、理解、生成等部分。

自然语言认知和理解是让计算机把输入的语言变成有意思的符号和关系,然后根据目的再进行处理。 所以简单来说,自然语言处理就是让计算机理解人类语言。为了达到这样的目的,我们需要在理论上基于 数学和统计理论建立各种自然语言,然后通过计算机来实现这些语言模型。因为人类语言的多样性和复杂性, 所以总体而言自然语言处理是一门机具挑战的学科和领域。

NLP 是个非常庞杂的领域了,学习和应用起来都颇有难度。难度体现在语言场景、学习算法和语料等三个方面。

  • 语言场景是指人类语言的多样性、复杂性和歧义性;
  • 学习算法指的是 NLP 的数理模型一般都较为难懂,比如隐马尔科夫模型(HMM)、条件随机场(CRF)以及基于 RNN 的深度学习模型;
  • 语料的困难性指的是如何获取高质量的训练语料;

NLP 的模型基本包括两类,一类是基于概率图方法的模型,主要包括贝叶斯网络、马尔科夫链、隐马尔科夫模型、EM 算法、CRF 条件随机场、 最大熵模型等模型,在深度学习兴起之前,NLP 基本理论模型基础都是靠概率图模型撑起来的;另一类则是基于 RNN 的深度学习方法, 比如词嵌入、词向量、word2vec 等概念、基于 RNN 的机器翻译等等。

1.什么是 NLP?

1.1 NLP 的概念

  • 1.NLP (Natural Language Processing, 自然语言处理) 是计算机领域以及人工智能领域的一个重要的研究方向, 它研究用计算机来处理、理解以及运用人类语言, 达到人与计算机之间的有效通讯.

  • 2.NLP 研究表示语言能力、语言应用的模型, 通过建立计算机框架来实现这样的语言模型, 并且不断完善这样的语言模型, 还需要根据该语言模型来设计各种使用的系统, 并且探讨这些实用技术的评测技术.

  • 3.从自然语言的角度出发, NLP 结构如下:

    • 自然语言理解(Natural Language Understanding, NLU)

      • 音系学: 指代语言中发音的系统化组织
      • 词态学: 研究单词构成以及相互之间的关系
      • 句法学: 给定文本的那本分是语法正确的
      • 语义学: 给定文本的含义是什么
      • 语用学: 文本的目的是什么
    • 自然语言生成(Natural Language Generation, NLG)

      • 自然语言文本

1.2 NLP 的研究任务

  • 机器翻译: 计算机具备将一种语言翻译成另一种语言的能力
  • 情感分析: 计算机能够判断用户评论是否积极
  • 智能问答: 计算机能够正确回答输入的问题
  • 文摘生成: 计算机能够准确归纳、总结并产生文本摘要
  • 文本分类: 计算机能够采集各种文章, 进行主题分析, 从而进行自动分类
  • 舆论分析: 计算机能够判断目前舆论的导向
  • 知识图谱: 知识点相互连接而成的语义网路

Note

  • (1)自然语言理解涉及语言、语境和各种语言形式的学科

  • (2)自然语言生成则从结构化数据中以读取的方式自动生成文本. 该过程主要包含三个阶段:

    • 文本规划(完成机构化数据中的基础内容规划)
    • 语句规划(从结构化数据中组合语句来表达信息流)
    • 实现(产生语法通顺的语句来表达文本)

2.NLP 相关知识的构成

  • 基本术语

    • 分词(segment)

      • 分词常用的方法是基于字典的最长串匹配, 但是歧义分词很难
    • 词性标注(part-of-speech tagging)

      • 词性一般是指动词(noun)、名词(verb)、形容词(adjective)等
      • 标注的目的是表征词的一种隐藏状态, 隐藏状态构成的转移就构成了状态转义序列
    • 命名实体识别(NER, Named Entity Recognition)

      • 命名实体是指从文本中识别具有特定类别的实体, 通常是名词
    • 句法分析(syntax parsing)

      • 句法分析是一种基于规则的专家系统.
      • 句法分析的目的是解析句子中各个成分的依赖关系, 所以, 往往最终生成的结果是一棵句法分析树.
      • 句法分析可以解决传统词袋模型不考虑上下文的问题.
    • 指代消解(anaphora resolution)

      • 中文中代词出现的频率很高, 它的作用是用来表征前文出现过的人名、地名等
    • 情感识别(emotion recognition)

      • 情感识别本质上是分类问题. 通常可以基于词袋模型+分类器, 或者现在流行的词向量模型+RNN
    • 纠错(correction)

      • 基于 N-Gram 进行纠错、通过字典树纠错、有限状态机纠错
    • 问答系统(QA system)

      • 问答系统往往需要语言识别、合成、自然语言理解、知识图谱等多项技术的配合才会实现得比较好
  • 知识结构

    • 句法语义分析
    • 关键词抽取
    • 文本挖掘
    • 机器翻译
    • 信息检索
    • 问答系统
    • 对话系统
    • 文档分类
    • 自动文摘
    • 信息抽取
    • 实体识别
    • 舆情分析
    • 机器写作
    • 语音识别
    • 语音合成

3.NLP 的三个层面

  • 词法分析

    • 分词
    • 词性标注
  • 句法分析

    • 短语结构句法体系
    • 依存结构句法体系
    • 深层文法句法分析
  • 语义分析

    • 语义角色标注(semantic role labeling)

4.NLP 常用语料库

NLP–分词

1.中文分词

  • 在语言理解中, 词是最小的能够独立活动的有意义的语言成分. 将词确定下来是理解自然语言的第一步, 只有跨越了这一步, 中文才能像英文那样过渡到短语划分、概念抽取以及主题分析, 以致自然语言理解, 最终达到智能计算的最高境界.

  • 的概念一直是汉语言语言学界纠缠不清而又绕不开的问题. 主要难点在于汉语结构与印欧体系语种差异甚大, 对词的构成边界方面很难进行界定.

    • 在英语中, 单词本身就是 的表达, 一篇英文文章就是 单词 加分隔符(空格)来表示的.

    • 在汉语中, 词以字为基本单位的, 但是一篇文章的语义表达却仍然是以词来划分的. 因此, 在处理中文文本时, 需要进行分词处理, 将句子转化为词的表示. 这个切词处理过程就是 中文分词, 它通过计算机自动识别出句子的词, 在词间加入边界标记符, 分隔出各个词汇.

      • 整个过程看似简单, 然而实践起来却很复杂, 主要的困难在于 分词歧义
      • 其他影响分词的因素是: 未登录词分词粒度粗细
  • 自中文自动分词被提出以来, 历经将近 30 年的探索, 提出了很多方法, 可主要归纳为:

    • 规则分词
    • 统计分词
    • 混合分词(规则+统计)

1.1 规则分词

基于规则的分词是一种机械的分词方法, 主要通过维护词典, 在切分语句时, 将语句的每个字符串与词汇表中的词逐一进行匹配, 找到则切分, 否则不予切分.

按照匹配切分的方式, 主要有:

  • 正向最大匹配法
  • 逆向最大匹配法
  • 双向最大匹配法

Note

基于规则的分词,一般都较为简单高效,但是词典的维护是一个很庞大的工程。 在网络发达的今天,网络新词层出不穷,很难通过词典覆盖到所有词。

1.1.1 正向最大匹配法
  • 正向最大匹配法思想:

    • 正向最大匹配(Maximum Match Method, MM 法)的基本思想为:假定分词词典中的最长词有 \(i\) 个汉字字符, 则用被处理文档的当前字符串中的前 \(i\) 个字符作为匹配字段,查找字典。

      • 若字典中存在这样的一个 \(i\) 字词,则匹配成功,匹配字段被作为一个词切分出来;
      • 若字典中找不到这样一个 \(i\) 字词,则匹配失败,将匹配字段中的最后一个字去掉, 对剩下的字符串重新进行匹配处理;
    • 如此进行下去,直到匹配成功,即切分出词或剩余字符串的长度为零为止。这样就完成了一轮匹配, 然后取下一个 \(i\) 字字符串进行匹配处理,直到文档被扫描完为止。

  • 正向最大匹配算法描述如下:

    • (1)从左向右取待切分汉语句的 \(m\) 个字符作为匹配字段,\(m\) 为机器词典中最长词条的字符数

    • (2)查找机器词典并进行匹配。

      • 若匹配成功,则将这个匹配字段作为一个词切分出来
      • 若匹配不成功,则将这个匹配字段的最后一个字去掉,剩下的字符串作为新的匹配字段,进行再次匹配, 重复以上过程,直到切分出所有词为止
  • 正向最大匹配法示例:

    # -*- coding: utf-8 -*-
    
    class MM(object):
       """
       正向最大匹配
       """
       def __init__(self):
          self.window_size = 3
    
       def cut(self, text):
          result = []
          index = 0
          text_length = len(text)
          dic = ["研究", "研究生", "生命", "命", "的", "起源"]
          while text_length > index:
                for size in range(self.window_size + index, index, -1):
                   piece = text[index:size]
                   if piece in dic:
                      index = size - 1
                      break
                index = index + 1
                result.append(piece + "----")
          print(result)
    
    if __name__ == "__main__":
       text = "研究生命的起源"
       tokenizer = MM()
       print(tokenizer.cut(text))
    
1.1.2 逆向最大匹配法
  • 逆向最大匹配法思想:

    • 逆向最大匹配法(Reverse Maximum Match Method, RMM)的基本原理与正向最大匹配法相同, 不同的是分词切分的方向与正向最大匹配法相反。
    • 逆向最大匹配法从被处理文档的末端开始匹配扫描,每次取最末端的 \(i\) 个字符(\(i\) 为词典中最长词数) 作为匹配字段,若匹配失败,则去掉匹配字段最前面的一个字,继续匹配。相应地,它使用的分词词典是逆序词典, 其中的每个词条都按逆序存放。
    • 在实际处理时,先将文档进行倒排处理,生成逆序文档。然后,根据逆序词典,对逆序文档正向最大匹配法处理即可。 由于汉语中偏正结构较多,若从后向前匹配,可以适当提高精确度。所以,逆向最大匹配法比正向最大匹配法的误差 要小。统计结果表明,单纯使用正向最大匹配的错误率为 1/169,单纯使用逆向最大匹配的错误率为 1/245.
  • 逆向最大匹配法示例:

    # -*- coding: utf-8 -*-
    
    class RMM(object):
       """
       逆向最大匹配法
       """
       def __init__(self):
          self.window_size = 3
    
       def cut(self, text):
          result = []
          index = len(text)
          dic = ["研究", "研究生", "生命", "命", "的", "起源"]
          while index > 0:
                for size in range(index - self.window_size, index):
                   piece = text[size:index]
                   if piece in dic:
                      index = size + 1
                      break
                index = index - 1
                result.append(piece + "----")
          result.reverse()
          print(result)
    
    if __name__ == "__main__":
       text = "研究生命的起源"
       RMM_tokenizer = RMM()
       print(RMM_tokenizer.cut(text))
    
1.1.3 双向最大匹配法
  • 双向最大匹配法思想:

    • 双向最大匹配法(Bi-direction Matching Method)是将正向最大匹配法得到的分词结果和逆向最大匹配法得到的结果进行比较, 然后按照最大匹配原则,选取词数切分最少的作为结果。
  • 双向最大匹配的规则是:

    • (1)如果正、反向分词结果词数不同,则取分词数量较少的那个

    • (2)如果分词结果词数相同:

      • a.分词结果相同,就说明没有歧义,可返回任意一个
      • b.分词结果不同,返回其中单字较少的那个
  • 双向最大匹配法示例:

    # -*- coding: utf-8 -*-
    
    #TODO
    class BMM(object):
       """
       双向最大匹配法
       """
       def __init__(self):
          pass
    
       def cut(self, text):
          pass
    
    if __init__ == "__main__":
       text = "研究生命的起源"
       BMM_tokenizer = BMM()
       print(BMM_tokenizer.cut(text))
    

1.2 统计分词

随着大规模语料库的建立,统计机器学习方法的研究和发展,基于统计的中文分词算法逐渐成为主流。

  • 统计分词的主要思想是:

    • 把每个词看做是由词的最小单位的各个字组成的, 如果相连的字在不同的文本中出现的次数越多, 就证明这相连的字很可能就是一个词. 因此我们就可以利用 字与字相邻出现的频率 来反应 成词的可靠度, 统计语料中相邻共现的各个字的组合的频度, 当组合频度高于某一个临界值时,便可以认为此字组成会构成一个词语.
  • 基于统计的分词, 一般要做如下两步操作:

    • (1)建立统计语言模型
    • (2)对句子进行单词划分,然后对划分结果进行概率计算,获得概率最大的分词方式。这里就用到了统计学习算法, 如隐式马尔科夫(HMM)、条件随机场(CRF)等
1.2.1 语言模型

语言模型在信息检索、机器翻译、语音识别中承担着重要的任务。用概率论的专业术语描述语言模型就是:

  • 为长度为 \(m\) 的字符串确定其概率分布 \(P(\omega_{1}, \omega_{2}, \cdot, \omega_{m})\), 其中 \(\omega_{1}\)\(\omega_{m}\) 依次表示文本中的各个词语。一般采用链式法计算其概率值:

    \[P(\omega_{1}, \omega_{2}, \cdots, \omega_{m})=\]
    \[P(\omega_{1})P(\omega_{2}|\omega_{1})P(\omega_{3}|\omega_{1}, \omega_{2}) \cdots P(\omega_{i}|\omega_{1}, \omega_{2}, \cdots, \omega_{i-1}) \cdots P(\omega_{m}|\omega_{1}, \omega_{2}, \cdots, \omega_{m-1})\]
  • \(n\) 元模型(n-gram model)

    • 当文本过长时,公式右部从第三项起的每一项计算难度都很大。为了解决该问题,有人提出了 \(n\) 元模型(n-gram model) 降低该计算难度。 所谓 \(n\) 元模型就是在估算条件概率时,忽略距离大与等于 \(n\) 的上下文词的影响,因此:

      \[P(\omega_{i}|\omega_{1}, \omega_{2}, \cdots, \omega_{i-1}) = P(\omega_{i}|\omega_{i-(n-1)}, \omega_{i-(n-2)}, \cdots, \omega_{i-1})\]
    • \(n=1\) 时,称为一元模型(unigram model),此时整个句子的概率可以表示为:

      \[P(\omega_{1}, \omega_{2}, \cdots, \omega_{m}) = P(\omega_{1})P(\omega_{2}) \cdots P(\omega_{m})\]
      • 在一元模型中,整个句子的概率等于各个词语概率的乘积,即各个词之间都是相互独立的, 这无疑是完全损失了句中的词序信息,所以一元模型的效果并不理想.
    • \(n=2\) 时,称为二元模型(bigram model),概率的计算变为:

      \[P(\omega_{i}|\omega_{1}, \omega_{2}, \cdots, \omega_{i-1}) = P(\omega_{i}|\omega_{i-1})\]
    • \(n=3\) 时,称为三元模型(trigram model),概率的计算变为:

      \[P(\omega_{i}|\omega_{1}, \omega_{2}, \cdots, \omega_{i-1}) = P(\omega_{i}|\omega_{i-2},\omega_{i-1})\]
    • \(n \geq 2\) 时,该模型是可以保留一定的词序信息的,而且 \(n\) 越大,保留的词序信息越丰富,但计算成本也呈指数级增长。 一般使用频率计数的比例来计算 \(n\) 元条件概率:

      \[P(\omega_{i}|\omega_{i-(n-1)}, \omega_{i-(n-2)}, \cdots, \omega_{i-1}) = \frac{count(\omega_{i-(n-1)}, \cdots, \omega_{i-1},\omega_{i})}{count(\omega_{i-(n-1)}, \cdots, \omega_{i-1})}\]
      • 其中, \(count(\omega_{i-(n-1)}, \cdots, \omega_{i-1})\) 表示词语 \(\omega_{i-(n-1)}, \cdots, \omega_{i-1}\) 在语料库中出现的总次数
    • 综上,当 \(n\) 越大时,模型包含的词序信息越丰富,同时计算量随之增大。与此同时,长度越长的文本序列出现的次数也会越少,这样,按照上式估计 \(n\) 元条件概率时, 就会出现分子、分母为零的情况。因此,一般在 \(n\) 元模型中需要配合相应的平滑算法解决该问题,如拉普拉斯平滑算法等。

1.2.2 HMM 模型

隐马尔科夫模型(HMM)是将分词作为字在字符串中的序列标注任务来实现的。

  • 隐马尔科夫模型的基本思路是:

    • 每个字在构造一个特定的词语时都占据着一个确定的构词位置(即词位),现规定每个字最多只有四个构词位置:

      • B(词首)
      • M(词中)
      • E(词尾)
      • S(单独成词)
    • 用数学抽象表示如下:

      • \(\lambda = \lambda_{1}\lambda_{2}\lambda_{n}\) 代表输入的句子,\(n\) 为句子长度, \(\lambda_{i}\) 表示字, \(o=o_{1}o_{2} \cdots o_{n}\) 代表输出的标签,那么理想的输出即为:

        \[max = max P(o_{1}o_{2} \cdots o_{n}|\lambda_{1}\lambda_{2} \cdots \lambda_{n})\]
      • 在分词任务上, \(o\) 即为 B、M、E、S 这四种标记, \(\lambda\) 为诸如 “中”、“文” 等句子中的每个字(包括标点等非中文字符).

      • 需要注意的是, \(P(o|\lambda)\) 是关于 2n 个变量的条件概率,且 n 不固定。因此,几乎无法对 \(P(o|\lambda)\) 进行精确计算。 这里引入观测独立性假设,即每个字的输出仅仅与当前字有关,于是就能得到下式:

        \[P(o_{1}o_{2} \cdots o_{n}|\lambda_{1}\lambda_{2} \cdots \lambda_{n}) = p(o_{1}|\lambda_{1})p(o_{2}|\lambda_{2}) \cdots p(o_{n}|\lambda_{n})\]
  • 示例:

    • 下面句子(1)的分词结果就可以直接表示成如(2)所示的逐字标注形式:

      (1)中文 / 分词 / 是 /. 文本处理 / 不可或缺 / 的 / 一步! (2)中/B 文/E 分/B 词/E 是/S 文/B 本/M 处/M 理/E 不/B 可/M 或/M 缺/E 的/S 一/B 步/E!/S

1.2.3 其他统计分词算法
  • 条件随机场(CRF)也是一种基于马尔科夫思想的统计模型。

    • 在隐马尔科夫模型中,有个很经典的假设,就是每个状态只与它前面的状态有关。这样的假设显然是有偏差的, 于是,学者们提出了条件随机场算法,使得每个状态不止与它前面的状态有关,还与它后面的状态有关。
  • 神经网络分词算法是深度学习方法在 NLP 上的应用。

    • 通常采用 CNN、LSTM 等深度学习网络自动发现一些模式和特征,然后结合 CRF、softmax 等分类算法进行分词预测。
  • 对比于机械分词法,这些统计分词方法不需要耗费人力维护词典,能较好地处理歧义和未登录词,是目前分词中非常主流的方法。 但其分词的效果很依赖训练预料的质量,且计算量相较于机械分词要大得多。

1.3 混合分词

事实上,目前不管是基于规则的算法、基于 HMM、CRF 或者 deep learning 等的方法, 其分词效果在具体任务中,其实差距并没有那么明显。

在实际工程应用中,多是基于一种分词算法,然后用其他分词算法加以辅助。最常用的方式就是先基于词典的方式进行分词, 然后再用统计方法进行辅助。如此,能在保证词典分词准确率的基础上,对未登录词和歧义词有较好的识别。

jieba 分词工具就是基于这种方法的实现。

2.外文分词

3.jieba 分词

3.1 安装

$ pip install paddlepaddle-tiny=1.6.1 # Python3.7
$ pip install jieba

3.2 特点、算法

  • 特点:

    • 支持四种分词模式:

      • 精确模式:试图将句子最精确地切开,适合文本分析

      • 全模式:把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义

      • 搜索引擎模式:在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词

      • paddle 模式:利用 PaddlePaddle 深度学习框架,训练序列标注(双向GRU)网络模型实现分词。同时支持词性标注

        • paddle 模式使用需安装 paddlepaddle-tiny

          $ pip install paddlepaddle-tiny=1.6.1
          
        • 目前 paddle 模式支持 jieba v0.40 及以上版本,jieba v0.40以下版本,请升级 jieba

          $ pip install jieba --upgrade
          
    • 支持繁体分词

    • 支持自定义词典

    • MIT 授权协议

  • 算法:

    • 基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG)
    • 采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
    • 对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法

3.3 分词

  • API

    • jieba.enable_paddle()

    • jieba.cut(sentence = "", cut_all = False, HMM = True, use_paddle = False)

    • jieba.lcut(sentence = "", cut_all = False, HMM = True, use_paddle = False)

    • jieba.cut_for_search(sentence = "", HMM = True)

    • jieba.lcut_for_search(sentence = "", HMM = True)

    • jieba.Tokenizer(dictionary = DEFAULT_DICT)

      • 新建自定义分词器,可用于同时使用不同词典,jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射

3.4 添加自定义词典

开发者可以指定自己自定义的词典,以便包含 jieba 词库里没有的词。虽然 jieba 有新词识别能力,但是自行添加新词可以保证更高的正确率。

  • 用法

    • 1.创建新的词典

      • 词典格式:

        • 文件名: dict.txt
        • 文件格式: 若为路径或二进制方式打开的文件,则文件必须为 UTF-8 编码
        • 一个词占一行
        • 每一行分三部分:词语、词频(可省略)、词性(可省略),用空格分隔开顺序不可颠倒
    • 2.使用 jieba.load_userdict(file_name) 载入自定义词典

    • 3.更改分词器(默认为 jieba.dt)的 tmp_dircache_file 属性,可分别指定缓存文件所在的文件夹及其文件名,用于受限的文件系统

  • API

    • jieba.load_userdict(file_name)

      • 载入自定义词典
    • jieba.dt.tmp_dir

    • jieba.dt.cache_file

  • 示例

    import sys
    import jieba
    import jieba.posseg as pseg
    sys.path.append("./util_data")
    jieba.load_userdict("./util_data/userdict.txt")
    
    jieba.add_word("石墨烯")
    jieba.add_word("凱特琳")
    jieba.add_word("自定义词")
    
    test_sent = (
       "李小福是创新办主任也是云计算方面的专家; 什么是八一双鹿\n"
       "例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类\n"
       "「台中」正確應該不會被切開。mac上可分出「石墨烯」;此時又可以分出來凱特琳了。"
    )
    words = jieba.cut(test_sent)
    print(" ".join(words))
    

3.5 调整词典

  • API

    • add_word(word, freq = None, tag = None)
    • del_word(word)
    • suggest_freq(segment, tune - True)
  • 示例

    string = "如果放到旧字典中将出错。"
    seg_list = jieba.cut(string, HMM = False)
    print(" ".join(seg_list))
    jieba.suggest_freq(segment = ("中", "将"), tune = True)
    seg_list_tuned = jieba.cut(string, HMM = False)
    print(" ".join(seg_list_tuned))
    

3.5 关键词提取

3.5.1 基于 TF-IDF 算法的关键词提取
  • API

    • jieba.analyse.extract_tags(sentence, topK = 20, withWeight = False, allowPOS = ())

    • jieba.analyse.TFIDF(idf_path = None

      • 新建 TF-IDF 实例,idf_path 为 IDF 频率文件
  • 用法

    • 关键词提取所使用的逆向文档频率(IDF)文本语料库可以切换成自定义语料库的路径

    • 关键词提取所使用停止词(Stop Words)文本语料库可以切换成自定义语料库的路径

3.5.2 基于 TextRank 算法的关键词提取
  • API

    • jieba.analyse.textrank(sentence, topK = 20, withWeight = False, allowPOS = ("ns", "n", "vn", "v"))
    • jieba.analyse.TextRank()
  • 算法论文

  • 基本思想:

    • 1.将待抽取关键词的文本进行分词
    • 2.以固定窗口大小(默认为5,通过span属性调整),词之间的共现关系,构建图
    • 3.计算图中节点的PageRank,注意是无向带权图
  • 使用示例:

    • test

3.6 词性标注

3.7 并行分词

3.8 Tokenize:返回词语在原文的起止位置

3.9 ChineseAnalyzer for Whoosh 搜索引擎

  • API

    • from jieba.analyse import ChineseAnalyzer
  • 示例

    
    

3.10 命令行分词

  • 语法

    $ python -m jieba [option] filename
    
  • 示例

    $ python -m jieba news.txt > cut_result.txt
    
  • 命令行选项

    • filename
    • python -m jieba -h, –help
    • -d [DELIM], –delimiter [DELIM]
    • -p [DELIM], –pos [DELIM]
    • -D DICT
    • -a, –cut-all
    • -n, –no-hmm
    • -q, –quiet
    • -V, –version

4.其他分词

  • 常用分词库
    • StanfordNLP
    • 哈工大语言云
    • 庖丁解牛分词
    • 盘古分词 (ICTCLAS, 中科院汉语词法分析系统)
    • IKAnalyzer(Luence项目下,基于java)
    • FudanNLP(复旦大学)
    • 中文分词工具
    • Ansj
    • 盘古分词
    • jieba

NLP–词性标注

1.词性标注介绍

  • 词性 是词汇基本的语法属性, 通常称为 词类.

  • 词性标注(part-of-speech tagging) 是在给定句子中判断每个词的语法范畴, 确定其词性并加以标注的过程. 例如:

    • 表示人、地点、事物以及其他抽象概念的名称即为 名词
    • 表示动作或状态变化的词为 动词
    • 描述或修饰名词属性、状态的词为 形容词
  • 词性标注最简单的方法是从预料库中统计每个词对应的高频词性, 将其作为默认的词性. 但这样显然还有提升空间. 目前较为主流的方法是如同分词一样, 将句子的词性标注作为一个序列标注问题来解决, 那么分词中常用的手段, 如隐含马尔科夫模型, 条件随机场模型等皆可在词性标注任务中使用.

Note

  • 在中文中, 一个词的词性很多时候都不是固定的, 一般表现为同音同形的词在不同场景下, 其表示的语法截然不同, 这就为词性标注带来很大的困难; 但是另外一方面, 从整体上看, 大多数词语, 尤其是实词, 一般只有一到两个词性, 且其中一个词性的使用频次远远大于另一个, 即使每次将高频词性选择进行标注, 也能实现 80% 以上的准确率. 如此,若我们对常用的词性能够进行很好地识别, 那么就能够覆盖绝大多数场景, 满足基本的准确度要求.

2.词性标注规范

  • 词性标注需要有一定的标注规范, 如将词分为名词、动词、形容词, 然后用 nvadj 等来进行表示.

  • 中文领域中尚无统一的标注标准, 较为主流的是以下两类,两类标注方式各有千秋,一般任选一种方式即可:

    • 北大词性标注集
    • 宾州词性标注集

2.1 北大词性标注集

  • 北大词性标注规范表
标记 词性 说明
ag 形语素 形容词性语素. 形容词代码为 a, 语素代码 g 前面置以 a
a 形容词 取英语形容词 adjective 的第 1 个字母
ad 副形词 直接作状语的形容词. 形容词代码 a 和副词代码 d 并在一起
an 名形词 具有名词功能的形容词. 形容词代码 a 和名词代码 n 并在一起
b 区别词 取汉字“别”的声母
c 连词 取英语连词 conjunction 的第 1 个字母
dg 副语素 副词性语素.副词代码为 d, 语素代码 g 前面置以 d
d 副词 取 adverb 的第 2 个字母, 因其第 1 个字母已用于形容词
e 叹词 取英语叹词 exclamation 的第 1 个字母
f 方位词 取汉字“方”的声母
g 语素 绝大多数语素都能作为合成词的“词根”, 取汉字“根”的声母
h 前接成分 取英语 head 的第 1 个字母
i 成语 取英语成语 idiom 的第 1 个字母
j 简称略语 取汉字“简”的声母
k 后接成分  
l 习用语 习用语尚未成为成语, 有点“临时性”, 取“临”的声母
m 数词 取英语 numeral 的第 3 个字母, n ,u 已有他用
ng 名语素 名词性语素.名词代码为 n, 语素代码 g 前面置以 n
n 名词 取英语名词 noun 的第 1 个字母
nr 人名 名词代码 n 和“人(ren)”的声母并在一起
ns 地名 名词代码 n 和处所词代码 s 并在一起
nt 机构团体 “团”的声母为 t, 名词代码 n 和 t 并在一起
nz 其他专名 “专”的声母的第 1 个字母为 z, 名词代码 n 和 z 并在一起
o 拟声词 取英语拟声词 onomatopoeia 的第 1 个字母
p 介词 取英语介词 prepositional 的第 1 个字母
q 量词 取英语 quantity 的第 1 个字母
r 代词 取英语代词 pronoun 的第 2 个字母, 因 p 已用于介词
s 处所词 取英语 space 的第 1 个字母
Tg 时语素 时间词性语素. 时间词代码为 t, 在语素的代码 g 前面置以 t
t 时间词 取英语 time 的第1个字母
u 助词 取英语助词 auxiliary 的第 2 个字母, 因 a 已用于形容词
vg 动语素 动词性语素.动词代码为 v. 在语素的代码 g 前面置以 v
v 动词 取英语动词 verb 的第一个字母
vd 副动词 直接作状语的动词. 动词和副词的代码并在一起
vn 名动词 指具有名词功能的动词. 动词和名词的代码并在一起
w 标点符号  
x 非语素字 非语素字只是一个符号, 字母 x 通常用于代表未知数、符号
y 语气词 取汉字“语”的声母
z 状态词 取汉字“状”的声母的前一个字母

2.2 宾州词性标注集

标记 英语解释 中文解释
AD adverbs 副词
AS Aspect marker 体态词,体标记(例如:了,在,着,过)
BA 把 in ba-const “把”,“将”的词性标记
CC Coordinating conjunction 并列连词,“和”
CD Cardinal numbers 数字,“一百”
CS Subordinating conj 从属连词(例子:若,如果,如…)
DEC 的 for relative-clause etc “的”词性标记
DEG Associative 联结词“的”
DER in V-de construction, and V-de-R “得”
DEV before VP
DT Determiner 限定词,“这”
ETC Tag for words, in coordination phrase 等,等等
FW Foreign words 例子:ISO
IJ interjetion 感叹词
JJ Noun-modifier other than nouns  
LB in long bei-construction 例子:被,给
LC Localizer 定位词,例子:“里”
M Measure word(including classifiers) 量词,例子:“个”
MSP Some particles 例子:“所”
NN Common nouns 普通名词
NR Proper nouns 专有名词
NT Temporal nouns 时序词,表示时间的名词
OD Ordinal numbers 序数词,“第一”
ON Onomatopoeia 拟声词,“哈哈”
P Preposition (excluding 把 and 被) 介词
PN pronouns 代词
PU Punctuations 标点
SB in long bei-construction 例子:“被,给”
SP Sentence-final particle 句尾小品词,“吗”
VA Predicative adjective 表语形容词,“红”
VC Copula 系动词,“是”
VE 有 as the main verb “有”
VV Other verbs 其他动词

3.jieba 分词中的词性标注

3.1 jieba 词性标注基本思路

  • 类似分词流程, jieba 的词性标注同样是结合规则和统计的方式, 具体为在词性标注的过程中, 词典匹配和 HMM 共同作用。 词性标注流程如下:

    • 1.首先,基于正则表达式进行汉字判断, 正则表达式如下:

      import re
      
      re_han_internal = re.compile("([\u4E00-\u9FD5a-zA-Z0-9+#&\._]+)")
      
    • 2.若符合上面的正则表达式,则判定为汉字,然后基于前缀词典构建有向无环图,再基于有向无环图计算最大概率路径,同时在前缀词典中 找出它所分出的词性,若在词典中未找到,则赋予词性为 x (代表未知)。当然,若在这个过程中,设置使用 HMM,且待标注词为未登录词, 则会通过 HMM 方式进行词性标注。

    • 3.若不符合上面的正则表达式,那么将继续通过正则表达式进行类型判断,分别赋予 xm (数词)、eng (英文)

3.2 jieba 词性标注示例

# -*- coding: utf-8 -*-

import jieba.posseg as psg

def get_part_of_speech_taging(sentence, HMM = True):
   """
   词性标注
   Params:
      HMM=False: 非 HMM 词性标注
      HMM=True: HMM 词性标注
   """
   segment_list = psg.cut(sentence, HMM)
   tagged_sentence = " ".join([f"{w}/{t}" for w, t in segment_list])

   return tagged_sentence



if __name__ == "__main__":
   # data
   sentence = "中文分词是文本处理不可或缺的一步!"
   tagged_sentence = get_part_of_speech_taging(sentence)
   print(tagged_sentence)

Note

  • Jieba 分词支持自定义词典,其中的词频和词性可以省略。然而需要注意的是,若在词典中省略词性,采用 Jieba 分词进行词性标注后, 最终切分词的词性将变成 x,这在如语法分析或词性统计等场景下会对结果有一定的影响。因此,在使用 Jieba 分词设置自定义词典时, 尽量在词典中补充完整的信息.

NLP–命名实体识别

1.命名实体识别介绍

  • 命名实体识别(NER, Named Entity Recognition)

    • 与自动分词、词性标注一样,命名实体识别也是自然语言处理的一个基础任务,是信息抽取、信息检索、机器翻译、 问答系统等多种自然语言处理技术必不可少的组成部分。
  • 命名实体识别的目的

    • 识别语料中人名、地名、组织机构名等命名实体。由于这些命名实体数量不断增加,通常不可能在词典中穷尽列出, 且其构成方法具有各自的规律性,因此,通常把对这些词的识别在词汇形态处理(如汉语切分)任务中独立处理, 称为命名实体识别(Named Entities Recognition, NER)。
  • 命名实体识别研究的命名实体一般分为:

    • 3 大类:

      • 实体类
      • 时间类
      • 数字类
    • 7 小类:

      • 人名
      • 地名
      • 组织机构名
      • 时间
      • 日期
      • 货币
      • 百分比

    Note

    • 由于数量、时间、日期、货币等实体识别通常可以采用 模式匹配 的方式获得较好的识别效果, 相比之下人名、地名、机构名较复杂,因此近年来的研究主要以这几种实体为主。
  • 命名实体识别效果的评判主要看:

    • 实体的边界是否划分正确
    • 实体的类型是否标注正确
  • 中文命名实体识别:

    • 中文的命名实体识别相比英文挑战更大,在汉语中,相较于实体类别标注子任务,实体边界的识别更加困难,主要难点如下:

      • 各类命名实体的数量众多
      • 命名实体的构成规律复杂
      • 嵌套情况复杂
      • 长度不确定
  • 命名实体识别也划分为三种方法:

    • 基于规则

      • 规则
      • 词典
    • 基于统计

      • 隐马尔科夫模型
      • 最大熵模型
      • 条件随机场
    • 规则和统计混合

      • NLP 并不完全是一个随机过程,单独使用基于统计的方法是状态搜索空间非常庞大,必须借助规则知识提前进行过滤修建处理
  • 命名实体识别中目前的主流方法

    • 序列标注方式

2.基于条件随机场的命名实体识别

  • HMM 的局限性

    在 HMM 中,将分词作为字标注问题来解决,其中有两条非常经典的独立性假设:

    • 输出观察值之间严格独立
    • 状态的转移过程中当前状态只与前一状态有关(一阶马尔科夫模型)

    通过这两条假设,使得 HMM 的计算成为可能,模型的计算也简单许多。

    但多数场景下,尤其在大量真实语料中,观察序列更多的是以一种多重的交互特征形式表现出来, 观察元素之间广泛存在的长程相关性,这样 HMM 的效果就受到了制约。

  • CRF

    • 基于 HMM,20001 年,Lafferty 等学者提出了条件随机场,其主要思想来源于 HMM, 也是一种用来标记和切分序列化数据的统计模型。不同于 HMM 的是,条件随机场是在给定观察的标记序列下, 计算整个标记序列的联合概率,而 HMM 是在给定当前状态下,定义下一个状态的分布。
    • 条件随机场的定义

3.示例

3.1 日期识别

当针对结构化数据时,日期设置一般有良好的规范,在数据入库时予以类型约束,在需要时能够通过解析还原读取到对应的日期。 然而在一些非结构化的数据应用场景下,日期和文本混杂在一起,此时日期的识别就变得艰难许多。非结构化数据下的日期识别多是与具体需求有关。

  • 任务及背景:

    • 现有一个基于语音问答的酒店预定系统,其根据用户的每句语音进行解析,识别出用户的酒店预定需求,如房间型号、入住时间等; 用户的语音在发送给后台进行请求时已经转换成中文文本,然而由于语音转换工具的识别问题,许多日期类的数据并不是严格的数字, 会出现诸如 “六月 12” “2016年八月” “20160812” “后天下午”等形式。
    • 不关注问答系统的具体实现过程,主要目的是识别出每个请求文本中可能的日期信息,并将其转换成统一的格式进行输出。 例如:“我要今天住到明天”(假设今天为2017年10月1号)那么通过日期解析后,应该输出为 “2017-10-01” 和 “2017-10-02”。
  • 任务实现技术:

    • 正则表达式
    • Jieba 分词

(1)通过 Jieba 分词将带有时间信息的词进行切分,然后记录连续时间信息的词

  • Jieba 词性标注提取文本中

    • “m”: 数字
    • “t”: 时间
import re
from datetime import datetime, timedelta
from dateutil.parser import parse
import jieba.posseg as psg

def time_extract(text):
   time_res = []
   word = ""
   keyDate = {
      "今天": 0,
      "明天": 1,
      "后天": 2,
   }

   for key, value in psg.cut(text):
      if key in keyDate:
            if word != "":
               time_res.append(word)
            word = (datetime.today() + timedelta(days = keyDate.get(key, 0))).strftime("%Y年%m月%d日")
      elif word != "":
            if value in ["m", "t"]:
               word = word + key
            else:
               time_res.append(word)
               word = ""
      elif value in ["m", "t"]:
            word = key
   if word != "":
      time_res.append(word)
   result = list(filter(lambda x: x is not None, [check_time_valid(w) for w in time_res]))
   final_res = [parse_datetime(w) for w in result]

   return [x for x in final_res if x is not None]


def check_time_valid(word):
   m = re.match("\d+$", word)
   if m:
      if len(word) <= 6:
            return None
   word1 = re.sub("[号|日]\d+$", "日", word)
   if word1 != word:
      return check_time_valid(word1)
   else:
      return word1


def parse_datetime(msg):
   if msg is None or len(msg) == 0:
      return None

   try:
      dt = parse(msg, fuzzy=True)
      return dt.strftime('%Y-%m-%d %H:%M:%S')
   except Exception as e:
      m = re.match(
            r"([0-9零一二两三四五六七八九十]+年)?([0-9一二两三四五六七八九十]+月)?([0-9一二两三四五六七八九十]+[号日])?([上中下午晚早]+)?([0-9零一二两三四五六七八九十百]+[点:\.时])?([0-9零一二三四五六七八九十百]+分?)?([0-9零一二三四五六七八九十百]+秒)?",
            msg)
      if m.group(0) is not None:
            res = {
               "year": m.group(1),
               "month": m.group(2),
               "day": m.group(3),
               "hour": m.group(5) if m.group(5) is not None else '00',
               "minute": m.group(6) if m.group(6) is not None else '00',
               "second": m.group(7) if m.group(7) is not None else '00',
            }
            params = {}

            for name in res:
               if res[name] is not None and len(res[name]) != 0:
                  tmp = None
                  if name == 'year':
                        tmp = year2dig(res[name][:-1])
                  else:
                        tmp = cn2dig(res[name][:-1])
                  if tmp is not None:
                        params[name] = int(tmp)
            target_date = datetime.today().replace(**params)
            is_pm = m.group(4)
            if is_pm is not None:
               if is_pm == u'下午' or is_pm == u'晚上' or is_pm =='中午':
                  hour = target_date.time().hour
                  if hour < 12:
                        target_date = target_date.replace(hour=hour + 12)
            return target_date.strftime('%Y-%m-%d %H:%M:%S')
      else:
            return None


UTIL_CN_NUM = {
   '零': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4,
   '五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
   '0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
   '5': 5, '6': 6, '7': 7, '8': 8, '9': 9
}
UTIL_CN_UNIT = {'十': 10, '百': 100, '千': 1000, '万': 10000}

def cn2dig(src):
   if src == "":
      return None
   m = re.match("\d+", src)
   if m:
      return int(m.group(0))
   rsl = 0
   unit = 1
   for item in src[::-1]:
      if item in UTIL_CN_UNIT.keys():
            unit = UTIL_CN_UNIT[item]
      elif item in UTIL_CN_NUM.keys():
            num = UTIL_CN_NUM[item]
            rsl += num * unit
      else:
            return None
   if rsl < unit:
      rsl += unit
   return rsl

def year2dig(year):
   res = ''
   for item in year:
      if item in UTIL_CN_NUM.keys():
            res = res + str(UTIL_CN_NUM[item])
      else:
            res = res + item
   m = re.match("\d+", res)
   if m:
      if len(m.group(0)) == 2:
            return int(datetime.datetime.today().year/100)*100 + int(m.group(0))
      else:
            return int(m.group(0))
   else:
      return None

text1 = '我要住到明天下午三点'
print(text1, time_extract(text1), sep=':')

text2 = '预定28号的房间'
print(text2, time_extract(text2), sep=':')

text3 = '我要从26号下午4点住到11月2号'
print(text3, time_extract(text3), sep=':')

text4 = '我要预订今天到30的房间'
print(text4, time_extract(text4), sep=':')

text5 = '今天30号呵呵'
print(text5, time_extract(text5), sep=':')

3.2 地名识别

地名识别中将采用基于条件随机场的方法进行地名识别任务. 条件随机场模型的实现需要先安装 CRF++,它是一款基于 C++ 高效实现 CRF 的工具。

  • 1.CRF++ 安装

  • 2.CRF++ Python 接口, 可以通过接口加载训练好的模型

    cd python
    python setup.py build
    sudo python setup.py install
    
  • 3.使用 CRF++ 进行地名识别

    • 任务及背景:

      • 采用的预料数据集,是 1998 年人民日报分词数据集,该语料数据集主要是一个词性标注集。 可以使用其中被标记为 “ns” 的部分来构造地名识别语料。如:”[香港/ns特别/a行政区/n]ns”, 可以提取出 “香港特别行政区(中括号以内的”ns”在这里不再单独作为一个地名)”。按照这种思路, 对人民日报语料进行数据分析,并切割了部分作为测试集进行验证。
    • (1)确定标签体系

      • 如同分词和词性标注一样,命名实体识别也有自己的标签体系。一般用户可以按照自己的想法自行设计, 如下为地理位置标记规范,即针对每个字标记为:”B”, “E”, “M”, “S”, “O” 中的一个:

        标注 含义
        B 当前词为地理命名实体的首部
        M 当前词为地理命名实体的内部
        E 当前词为地理命名实体的尾部
        S 当前词单独构成地理命名实体
        O 当前词不是地理命名实体或组成部分
    • (2)语料数据处理

      • CRF++ 的训练数据要求一定的格式,一般是一行一个 token,一句话由多行 token 组成,多个句子之间用空行分开。 其中每行又分成多列,除最后一列之外,其他列表示特征。因此一般至少需要两列,最后一列表示要预测的标签(“B”, “E”, “M”, “S”, “O”)。
      pass
      
    • (3)特征模板设计

    • (4)模型训练和测试

    • (5)模型使用

NLP–关键词提取

1.关键词提取介绍

关键词是代表文章重要内容的一组词。对文本聚类、分类、自动摘要等起重要作用。此外,它还能使人们便捷地浏览和获取信息。 类似于其他的机器学习方法,关键词提取算法一般也可以分为有监督和无监督两类:

  • 有监督的关键词提取方法主要是通过分类的方式进行,通过构建一个较为丰富和完善的词表,然后通过判断每个文档与词表中每个词的匹配程度, 以类似打标签的方式,达到关键词提取的效果。

    • 有监督的方法能够获取到较高的精度,但缺点是需要大批量的标注数据,人工成本过高
    • 另外,现在每天的信息增加过多,会有大量的新信息出现,一个固定的词表有时很难将新信息的内容表达出来, 但是要人工维护这个受控的词表却要很高的人力成本,这也是使用有监督方法来进行关键词提取的一个比较大的缺陷
  • 无监督的方法对数据的要求比较低,既不需要一张人工生成、维护的词表,也不需要人工标准语料辅助进行训练。 因此,这类算法在关键词提取领域的应用更受到大家的青睐。

    • TF-IDF 算法

    • TextRank 算法

    • 主题模型算法

      • LSA
      • LSI
      • LDA

2.关键词提取算法–TF-IDF 算法

TF-IDF 算法(Term Frequency-Inverse Document Frequency,词频-逆文档频次算法),是一种基于统计的计算方法, 常用于评估一个文档集中一个词对某份文档的重要程度。这种作用显然很符合关键字抽取的需求,一个词对文档越重要,那就越可能 是文档对的关键词,常将 TF-IDF 算法应用于关键词提取中。

从算法的名称就可以看出,TF-IDF 算法由两部分组成:

  • TF 算法

    • TF 算法是统计一个词在一篇文档中出现的频次,其基本思想是,一个词在文档中出现的次数越多,则其对文档的表达能力就越强
  • IDF 算法

    • IDF 算法则是统计一个词在文档集的多少个文档中出现,其基本思想是,如果一个词在越少的文档中出现,则其对文档的区分能力也就越强

TF 算法和 IDF 算法也能单独使用,在最早的时候就是如此,但在使用过程中,学者们发现这两种算法都有不足之处。TF 仅衡量词的出现频次, 但是没有考虑到词的对文档的区分能力。

  • TF 的计算常用表达式:

    \({tf}_{ij}=\frac{n_{ij}}{\sum_{k} n_{kj}}\)

    \(tf(word) = \frac{word在文档中出现的次数}{文档总词数}\)

    • 其中:

      • \(n_{ij}\) 表示词 \(i\) 在文档 \(j\) 中的出现频次

        • 但是仅用频次来表示,长文本中的词出现频次高的概率会更大,这一点会影响到不同文档之间关键词权值的比较,所以在计算的过程中一般还会对词频进行归一化
      • \(\sum_{k} n_{kj}\) 是统计文档中每个词出现次数的总和,也就是文档的总词数

  • IDF 的计算常用表达式:

    \({idf}_i=log\Big(\frac{|D|}{1+|D_{i}|}\Big)\)

    • 其中

      • \(|D|\) 为文档集中的总文档数
      • \(|D_{i}|\) 为文档集中出现词 \(i\) 的文档数量。分母加 1 是采用了拉普拉斯平滑, 避免有部分新的词没有在语料库中出现过而导致分母为零的情况出现,增强算法的健壮性

TF-IDF 算法就是 TF 算法与 IDF 算法的综合使用,具体的计算方法如下:

  • \(tf \times idf(i, j) = {tf}_{ij} \times {idf}_{i} = \frac{n_{ij}}{\sum_{k} n_{kj}} \times log\Big(\frac{|D|}{1+|D_{i}|}\Big)\)

Note

TF-IDF 算法也有很多变种的加权方法。传统的 TF-IDF 算法中,仅考虑了词的两个统计信息(出现频次、在多少个文档出现), 因此,其对文本的信息利用程度显然也是很少的。

除了上面的信息外,在一个文本中还有许多信息能够对关键词的提取起到很好的知道作用,例如每个词的词性、出现的位置等。

在某些特定的场景中,如在传统的 TF-IDF 基础上,加上这些辅助信息,能对关键词提取的效果起到很好的提高作用。

3.关键词提取算法–TextRank 算法

TextRank 算法的基本思想来源于 Google 的 PageRank 算法。PageRank 算法是 Google 创始人拉里·佩奇和谢尔盖·布林与 1997 年构建早期的 的搜索系统原型时提出的链接分析算法,该算法是他们用来评价搜索系统过覆盖网页重要性的一种重要方法,随着 Google 的成功,该算法也成为其他搜索 引擎和学术界十分关注的计算模型。

PageRank 算法是一种网页排名算法,其基本思想有两条:

  • (1)链接数量

  • (2)链接质量

    • 一个网页被一个越高权值的网页链接,也能表明这个网页月重要

4.关键词提取算法–LSA, LSI, LDA 算法

5.关键词提取示例

5.1 关键词提取使用的 Python 库

  • jieba

    • analyse 模块封装的 TextRank 算法
  • gensim

    • Gensim 是一款开源的第三方 Python 工具包,用于从原始的非结构化文本中,无监督的学习到文本隐层的主题向量表达。 它支持包括 TF-IDF、LSA、LDA 和 word2vec 在内的多种主题模型算法,支持流式训练,并提供了诸如相似度计算, 信息检索等一些常用任务的 API 接口
    pip install genism
    

5.2 关键词提取算法步骤

训练一个关键词提取算法步骤:

  • 1.加载已有的文档数据集
  • 2.加载停用词表
  • 3.对数据集中的文档进行 分词
  • 4.根据停用词表,过滤干扰词
  • 5.根据数据集训练关键词提取算法

根据训练好的关键词提取算法对新文档进行关键词提取步骤:

  • 1.对新文档进行分词
  • 2.根据停用词表,过滤干扰词
  • 3.根据训练好的算法提取关键词

NLP–句法分析

1.句法分析介绍

在自然语言处理中,机器翻译是一个重要的的课题,也是 NLP 应用的主要领域,而句法分析是机器翻译的核心数据结构。 句法分析是自然语言处理的核心技术,是对语言进行深层次理解的基石。

句法分析的主要任务是识别出句子所包含的 句法成分 以及这些 成分之间的关系,一般以 句法树 来表示 句法分析的结果。

句法分析一直是自然语言处理前进的巨大障碍,句法分析主要有以下两个难点:

  • 歧义

    • 自然语言区别于人工语言的一个重要特点就是它存在大量的歧义现象。人类自身可以依靠大量的先验知识有效地消除各种歧义, 而机器由于在知识表示和获取方面存在严重不足,很难像人类那样进行句法消歧。
  • 搜索空间

    • 句法分析是一个极为复杂的任务,候选树个数随句子增多呈指数级增长,搜索空间巨大。因此,必须设计出合适的解码器,以确保能能够在 可以容忍的时间内搜索到模型定义最优解。

句法分析(Parsing) 是从单词串得到句法结构的过程,而实现该过程的工具或程序被称为 句法分析器(Parser)。句法分析的种类很多, 根据其侧重目标将其分为:

  • 完全句法分析
  • 局部句法分析

句法分析中所用方法可以简单地分为两大类:

  • 基于规则的方法

    • 基于规则的方法在处理大规模真实文本时,会存在语法规则覆盖有限、系统可迁移等缺陷
  • 基于统计的方法

    • 随着大规模标注树库的建立,句法分析器的性能不断提高,最经典的就是 PCFG(Probabilistic Context Free Grammar), 它在句法分析领域得到了极大的应用,也是现在句法分析中常用的方法。
    • 统计句法分析模型本质上是一套面向候选树对的评价方法,其会给正确的句法树赋予一个较高的分值,而给不合理的句法树赋予一个较低的分值 这样就可以借用候选句法树的分值进行消歧。

2.句法分析的数据集与评测方法

统计分析方法一般都离不开语料数据集和相应的评价体系的支撑。

2.1 句法分析的数据集

2.2 句法分析的评测方法

2.3 句法分析的常用方法

NLP–文本向量化

1.概述

1.1 词汇表征

在谈词嵌入和词向量等词汇表征方法之前,我们先来看一下将 NLP 作为监督机器学习任务时该怎样进行描述。 假设以一句话为例:”I want a glass of orange ____.”。我们要通过这句话的其他单词来预测划横线部分的单词。 这是一个典型的 NLP 问题,将其作为监督机器学习来看的话,模型的输入是上下文单词,输出是划横线的目标单词, 或者说是目标单词的概率,我们需要一个语言模型来构建关于输入和输出之间的映射关系。应用到深度学习上, 这个模型就是循环神经网络。

在 NLP 里面,最细粒度的表示就是词语,词语可以组成句子,句子再构成段落、篇章和文档。 但是计算机并不认识这些词语,所以我们需要对以词汇为代表的自然语言进行数学上的表征。 简单来说,我们需要将词汇转化为计算机可识别的数值形式,这种转化和表征方式目前主要有两种, 一种是传统机器学习中的 one-hot 编码方式,另一种则是基于神经网络的词嵌入技术。

  • 文本向量化:

    • 文本向量化又称为 “词向量模型”、“向量空间模型”,即将文本表示成计算机可识别的实数向量, 根据粒度大小不同,可将文本特征表示分为字、词、句子、篇章几个层次
    • 文本向量化方法一般称为词嵌入(word embedding)方法,词嵌入这个说法很形象,就是把文本中的词嵌入到文本空间中, 用一个向量来表示词
  • 文本向量化的方法有很多:

    • 离散词向量表示

      • 基于规则、统计

        • 词集模型(Set of Word)

          • One-Hot encoding
          • 统计各词在句子中是否出现
        • 词袋模型(Bag of Word)

          • 统计各词在句子中出现的次数
        • Bi-gram、N-gram

        • TF-IDF

          • 统计各词在文档中的 TF-IDF 值(词袋模型 + IDF 值)
        • 共现矩阵

    • 分布式词向量表示

      • 基于神经网络的词嵌入

        • word2vec
        • doc2vec
        • str2vec

Note

Tomas Mikolov 2013 年在 ICLR 提出用于获取 word vector 的论文《Efficient estimation of word representations in vector space》, 文中简单介绍了两种训练模型 CBOW、Skip-gram,以及两种加速方法 Hierarchical Softmax、Negative Sampling。 除了 word2vec 之外,还有其他的文本向量化的方法,因此在这里做个总结.

2.为词向量模型建立词汇表

NLP 相关任务中最常见的第一步是创建一个 词表库 并把每个词顺序编号。

2.离散表示

文本向量化离散表示是一种基于规则和统计的向量化方式,常用的方法包括 词集模型词袋模型, 都是基于词之间保持独立性、没有关联为前提,将所有文本中单词形成一个字典,然后根据字典来统计单词出现频数, 不同的是:

  • 词集模型:

    • 统计各词在句子中是否出现
    • 例如 One-Hot Representation,只要单个文本中单词出现在字典中,就将其置为 1,不管出现多少次
  • 词袋模型:

    • 统计各词在句子中出现的次数
    • 只要单个文本中单词出现在字典中,就将其向量值加 1,出现多少次就加多少次

其基本的特点是忽略了文本信息中的语序信息和语境信息,仅将其反映为若干维度的独立概念, 这种情况有着因为模型本身原因而无法解决的问题,比如主语和宾语的顺序问题, 词袋模型天然无法理解诸如“我为你鼓掌”和“你为我鼓掌”两个语句之间的区别。

2.1 One-Hot Representation

  • One-Hoe 算法简介

    熟悉机器学习中分类变量的处理方法的同学对此一定很熟悉,无序的分类变量是不能直接硬编码为数字放入模型中的, 因为模型会自动认为其数值之间存在可比性,通常对于分类变量我们需要进行 one-hot 编码。 那么如何应用 one-hot 编码进行词汇表征呢?假设我们有一个包括 10000 个单词的词汇表, 现在需要用 one-hot 方法来对每个单词进行编码。以上面那句 “I want a glass of orange ____.” 为例, 假设 I 在词汇表中排在第 3876 个,那么 I 这个单词的 one-hot 表示就是一个长度为 10000 的向量, 这个向量在第 3876 的位置上为 1 ,其余位置为 0,其余单词同理,每个单词都是茫茫 0 海中的一个 1。大致如下图所示:

    _images/one_hot.png

    可见 one-hot 词汇表征方法最后形成的结果是一种稀疏编码结果,在深度学习应用于 NLP 任务之前, 这种表征方法在传统的 NLP 模型中已经取得了很好的效果。但是这种表征方法有两个缺陷: 一是容易造成维数灾难,10000 个单词的词汇表不算多,但对于百万级、千万级的词汇表简直无法忍受。 第二个则在于这种表征方式不能很好的词汇与词汇之间的相似性,比如上述句子, 如果我们已经学习到了 “I want a glass of orange juice.”,但如果换成了 “I want a glass of apple ____.”, 模型仍然不会猜出目标词是 juice。因为基于 one-hot的表征方法使得算法并不知道 apple 和 orange 之间的相似性, 这主要是因为任意两个向量之间的内积都为零,很难区分两个单词之间的差别和联系。

  • One-hot 的优、缺点

    • 优点:简单快捷

    • 缺点:数据稀疏、耗时耗空间、不能很好地展示词与词之间的相似关系,且还未考虑到词出现的频率,因而无法区别词的重要性

      • One-hot 的第一个问题是:One-hot 的基本假设是词之间的语义和语法关系是相互独立的,仅仅从两个向量是无法看出两个词汇之间的关系的,这种独立性不适合词汇语义的运算;
      • One-hot 的第二个问题是:维度爆炸问题,随着词典规模的增大,句子构成的词袋模型的维度变得越来越大,矩阵也变得超稀疏,这种维度的爆增,会大大耗费计算资源。
  • One-Hot 算法示例

    1. 文本语料

      John likes to watch moives, Mary likes too.
      John also likes to watch football games.
      
    2. 基于上述两个文档中出现的单词,构建如下词典(dictionary)

      {
         "John": 1,
         "likes": 2,
         "to": 3,
         "watch": 4,
         "moives": 5,
         "also": 6,
         "football": 7,
         "games": 8,
         "Mary": 9,
         "too": 10,
      }
      
    3. 文本 One-Hot

      # John likes to watch movies, Mary likes too.
      
      John:     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      likes:    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
      to:       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
      watch:    [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
      movies:   [0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
      also:     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      football: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      games:    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      Mary:     [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
      too:      [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
      
      # John also likes to watch football games.
      
      John:     [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      likes:    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
      to:       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
      watch:    [0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
      movies:   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      also:     [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
      football: [0, 1, 0, 0, 0, 0, 1, 0, 0, 0]
      games:    [0, 1, 0, 0, 0, 0, 0, 1, 0, 0]
      Mary:     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      too:      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
      
  • One-Hot 算法 Python 实现

    import os
    import numpy as np
    import pandas as pd
    import jieba
    import config
    
    def doc2onthot_matrix(file_path):
       """
       文本向量化 One-Hot
          1.文本分词
       """
       # (1)读取待编码的文件
       with open(file_path, encoding = "utf-8") as f:
          docs = f.readlines()
    
       # (2)将文件每行分词,分词后的词语放入 words 中
       words = []
       for i in range(len(docs)):
          docs[i] = jieba.lcut(docs[i].strip("\n"))
          words += docs[i]
    
       # (3)找出分词后不重复的词语,作为词袋,是后续 onehot 编码的维度, 放入 vocab 中
       vocab = sorted(set(words), key = words.index)
    
       # (4)建立一个 M 行 V 列的全 0 矩阵,M 是文档样本数,这里是行数,V 为不重复词语数,即编码维度
       M = len(docs)
       V = len(vocab)
       onehot = np.zeros((M, V))
       for i, doc in enumerate(docs):
          for word in doc:
             if word in vocab:
                pos = vocab.index(word)
                onehot[i][pos] = 1
       onehot = pd.DataFrame(onehot, columns = vocab)
       return onehot
    
    
    if __name__ == "__main__":
       corpus = os.path.join(config.data_dir, "corpus.txt")
       onehot = doc2onthot_matrix(corpus)
       print(onehot)
    
    from sklearn import DictVectorizer
    

2.2 词袋模型(Bag of Word)

  • 词袋模型算法

    • 对于句子、篇章,常用的离散表示方法是词袋模型,词袋模型以 One-Hot 为基础,忽略词表中词的顺序和语法关系, 通过记录词表中的每一个词在该文本中出现的频次来表示该词在文本中的重要程度,解决了 One-Hot 未能考虑词频的问题
    • 词袋(Bag Of Word) 模型是最早的以词语为基本单元的文本向量化方法。词袋模型,也称为计数向量表示(Count Vectors). 文档的向量表示可以直接使用单词的向量进行求和得到
  • 词袋模型示例

    1. 文本语料

      John likes to watch movies, Mary likes too.
      John also likes to watch football games.
      
    2. 基于上述两个文档中出现的单词,构建如下词典(dictionary)

      {
         "John": 1,
         "likes": 2,
         "to": 3,
         "watch": 4,
         "movies": 5,
         "also": 6,
         "football": 7,
         "games": 8,
         "Mary": 9,
         "too": 10,
      }
      
    3. 上面词典中包含 10 个单词,每个单词有唯一的索引,那么每个文本可以使用一个 10 维的向量来表示:

      John likes to watch movies, Mary likes too.  ->  [1, 2, 1, 1, 1, 0, 0, 0, 1, 1]
      John also likes to watch football games.     ->  [1, 1, 1, 1, 0, 1, 1, 1, 0, 0]
      
      文本 John likes to watch movies also football games Mary too
      John likes to watch movies, Mary likes too. [1, 2, 1, 1, 1, 0, 0, 0, 1, 1]
      John also likes to watch football games. [1, 1, 1, 1, 0, 1, 1, 1, 0, 0]
      • 横向来看,把每条文本表示成了一个向量
      • 纵向来看,不同文档中单词的个数又可以构成某个单词的词向量, 如: “John” 纵向表示成 [1, 1]
    4. 词袋模型优缺点

      • 优点:

        • 方法简单,当语料充足时,处理简单的问题如文本分类,其效果比较好
      • 缺点:

        • 数据稀疏、维度大
        • 无法保留词序信息
        • 存在语义鸿沟的问题
  • 词袋模型 Python 实现

    from sklearn import CountVectorizer
    
    count_vect = CountVectorizer(analyzer = "word")
    
    # 假定已经读进来 DataFrame,"text"列为文本列
    count_vect.fit(trainDF["text"])
    
    # 每行为一条文本,此句代码基于所有语料库生成单词的词典
    xtrain_count = count_vect.transform(train_x)
    

2.3 Bi-gram、N-gram

  • Bi-gram、N-gram 算法简介

    • 与词袋模型原理类似,Bi-gram 将相邻两个词编上索引,N-gram 将相邻 N 个词编上索引
  • Bi-gram、N-gram 算法示例

    1. 文本语料

      John likes to watch movies, Mary likes too.
      John also likes to watch football games.
      
    2. 基于上述两个文档中出现的单词,构建如下词典(dictionary)

      {
         "John likes": 1,
         "likes to": 2,
         "to watch": 3,
         "watch movies": 4,
         "Mary likes": 5,
         "likes too": 6,
         "John also": 7,
         "also likes": 8,
         "watch football": 9,
         "football games": 10,
      }
      
    3. 上面词典中包含 10 组单词,每组单词有唯一的索引,那么每个文本可以使用一个 10 维的向量来表示:

      John likes to watch movies. Mary likes too.  -> [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
      John also likes to watch football games.     -> [0, 1, 1, 0, 0, 0, 1, 1, 1, 1]
      
    4. Bi-gram、N-gram 优点

      • 考虑了词的顺序
    5. Bi-gram、N-gram 缺点

      • 词向量急剧膨胀
  • Bi-gram、N-gram 算法 Python 实现

    from . import .
    

2.4 TF-IDF

  • TF-IDF 算法简介

    • TF-IDF(词频-逆文档频率法,Term Frequency-Inverse Document Frequency) 作为一种加权方法, TF-IDF 在词袋模型的基础上对次出现的频次赋予 TF-IDF 权值,对词袋模型进行修正,进而表示该词在文档集合中的重要程度

      • 统计各词在文档中的 TF-IDF 值(词袋模型 + IDF 值)
      • 词袋模型、Bi-gram、N-gram 都是基于计数得到的,而 TF-IDF 则是基于频率统计得到的
      • 在利用 TF-IDF 进行特征提取时,若词 α 在某篇文档中出现频率较高且在其他文档中出现频率较低时, 则认为α可以代表该文档的特征,具有较好的分类能力,那么α作为特征被提取出来
    • TF-IDF 的分数代表了词语在当前文档和整个语料库中的相对重要性。TF-IDF 分数由两部分组成

      • TF(Term Frequency):词语频率

        \[TF(t) = \frac{词语在当前文档出现的次数}{当前文档中词语的总数}\]
        • TF 判断的是该字/词语是否是当前文档的重要词语,但是如果只用词语出现频率来判断其是否重要可能会出现一个问题, 就是有些通用词可能也会出现很多次,如:a、the、at、in 等,当然一般我们会对文本进行预处理时去掉这些所谓的停用词(stopwords), 但是仍然会有很多通用词无法避免地出现在很多文档中,而其实它们不是那么重要
      • IDF(Inverse Document Frequency):逆文档频率

        \[IDF(t) = log_{e} \Big(\frac{文档总数}{出现该词语的文档总数} \Big)\]
        • IDF 用于判断是否在很多文档中都出现了词词语,即很多文档或所有文档中都出现的就是通用词。 出现该词语的文档越多,IDF 越小,其作用是抑制通用词的重要性
      • 将上述求出的 TF 和 IDF 相乘得到的分数 TF-IDF,就是词语在当前文档和整个语料库中的相对重要性

      • TF-IDF 与一个词在当前文档中出现次数成正比,与该词在整个语料库中的出现次数成反比

  • TF-IDF 算法优缺点

    • 优点:

      • 简单快速,结果比较符合实际情况
    • 缺点:

      • 单纯以”词频”衡量一个词的重要性,不够全面,有时重要的词可能出现次数并不多
      • 无法体现词的位置信息,出现位置靠前的词与出现位置靠后的词,都被视为重要性相同,这是不正确的
  • TF-IDF 算法 Python 实现

    from sklearn import TfidfVectorizer
    from sklearn import HashingVectorizer
    
    # word level tf-idf
    tfidf_vect = TfidfVectorizer(analyzer = "word", token_pattern = r"\w{1,}", max_features = 5000)
    tfidf_vect.fit(trianDF["text"])
    xtrain_tfidf = tfidf_vect.transform(train_x)
    
    # n-gram level tf-idf
    tfidf_vect_ngram = TfidfVectorizer(analyzer = "word", token_pattern = r"\w{1,}", ngram_ragne(2, 3), max_features = 5000)
    tfidf_vect_ngram.fit(trainDF["text"])
    xtrain_tfidf = tfidf_vect.transform(train_x)
    

2.5 共现矩阵(Co-currence Matrix)

  • 共现矩阵算法简介

    • 共现:即共同实现,比如:一句话中共同出现,或一篇文章中共同出现

    • 共现矩阵构造时需要给出共同出现的距离一个规范– 窗口

      • 如果窗口宽度是 2,那就是在当前词的前后各 2 个词的范围内共同出现, 可以想象,其实是一个总长为 5 的窗口依次扫过所有文本,同时出现在其中的词就说它们共现
    • 当前词与自身不存在共现,共现矩阵实际上是对角矩阵

      • 实际应用中,用共现矩阵的一行(列)作为某个词的词向量,其向量维度还是会随着字典大小呈线性增长,而且存储共现矩阵可能需要消耗巨大的内存

      • 一般配合 PCA 或者 SVD 将其进行降维,比如:将 \(m \times n\) 的矩阵降维为 \(m \times r\),其中 \(r \le n\),即将词向量的长度进行缩减

        _images/SVG.png
  • 共现矩阵算法示例

    1. 文本语料

      John likes to watch movies.
      John likes to play basketball.
      
    2. 假设上面两句话设置窗口宽度为 1,则共现矩阵如下

      共现矩阵 John likes to watch moives play basketball
      John 0 2 0 0 0 0 0
      likes 2 0 2 0 0 0 0
      to 0 2 0 1 0 1 0
      watch 0 0 1 0 1 0 0
      moives 0 0 0 1 0 0 0
      play 0 0 1 0 0 0 1
      basketball 0 0 0 0 0 1 0
  • 共现矩阵算法 Python 实现

    from . import .
    

2.6 Count Vector

  • 假设

    • 语料 C,包含 D 个文档: \(\{d_{1}, d_{2}, \cdots, d_{D}\}\)
    • 语料 C 中的 N 个不重复词构成字典
    • Count Vector matrix(计算向量矩阵) \(M_{D \times N}\)
    • 计数向量矩阵 M 的第 \(i, i=1, 2, \cdots, D\) 行包含了字典中每个词在文档 \(d_{i}\) 中的频率
  • 示例

    • 语料 (D = 2):

      • \(d_1\) : He is a lazy boy. She is also lazy.
      • \(d_2\) : Neeraj is a lazy person.
    • 字典 (N = 6):

      • [“He”, “She”, “lazy”, “boy”, “Neeraj”, “person”]
    • 计数向量矩阵:

      CountVector He She lazy boy Neeraj person
      \(d_1\) 1 1 2 1 0 0
      \(d_2\) 0 0 1 0 1 1

3.分布式表示

词汇分布式表示最早由 Hinton 在 1986 年提出,其基本思想是:通过训练将每个词映射成 K 维实数向量(K 一般为模型中的超参数), 通过词之间的距离(如,consine 相似度、欧氏距离)来判断它们之间的语义相似度。

离散表示虽然能够进行词语或者文本的向量表示,进而用模型进行情感分析或者是文本分类之类的任务。 但其不能表示词语间的相似程度或者词语间的类比关系。

  • 比如:beautifule 和 pretty 两个词语,它们表达相近的意思,所以希望它们在整个文本的表示空间内挨得很近。

一般认为,词向量、文本向量之间的夹角越小,两个词相似度越高,词向量、文本向量之间夹角的关系用下面的余弦夹角进行表示:

\[\cos \theta = \frac{\overrightarrow{A} \cdot \overrightarrow{B}}{|\overrightarrow{A}| \cdot |\overrightarrow{B}|}\]

离散表示,如 One-Hot 表示无法表示上面的余弦关系,引入分布式表示方法,其主要思想是 用周围的词表示该词.

3.1 Word Embedding 简介

3.1.1 词嵌入简介

词嵌入的基本想法就是将词汇表中的每个单词表示为一个普通的向量,这个向量不像 One-Hot 向量那样都是 0 或者 1, 也没有 One-Hot 向量那样长,大概就是很普通的向量,比如长这样:[-0.91, 2, 1.8, -.82, 0.65, …]。 这样的一种词汇表示方式就像是将词嵌入到了一种数学空间里面,所以叫做词嵌入。其中,word2vec 使用的就是这种词嵌入技术的一种。

那么如何进行词嵌入呢?或者说我们如何才能将词汇表征成很普通的向量形式呢?这需要我们通过神经网络进行训练, 训练得到的网络权重形成的向量就是我们最终需要的东西,这种向量也叫词向量,word2vec 就是其中的典型技术。

word2vec 作为现代 NLP 的核心思想和技术之一,有着非常广泛的影响。word2vec 通常有两种语言模型, 一种是根据中间词来预测上下文的 skip-gram 模型,另一种是根据上下文来预测中间词的 CBOW (连续词袋模型)。

词汇表征问题解决之后,NLP 的核心便是建立语言模型。从深度学习的角度看,从深度学习的角度看, 假设我们将 NLP 的语言模型看作是一个监督学习问题:即给定上下文词 X,输出中间词 Y,或者给定中间词 X,输出上下文词 Y。 基于输入 X 和输出 Y 之间的映射便是语言模型。这样的一个语言模型的目的便是检查 X 和 Y 放在一起是否符合自然语言法则, 更通俗一点说就是 X 和 Y 搁一起是不是人话。

所以,基于监督学习的思想,本文的主角——word2vec 便是一种基于神经网络训练的自然语言模型。 word2vec 是谷歌于 2013 年提出的一种 NLP 工具,其特点就是将词汇进行向量化, 这样我们就可以定量的分析和挖掘词汇之间的联系。因而 word2vec 也是我们上一讲讲到的词嵌入表征的一种, 只不过这种向量化表征需要经过神经网络训练得到。

3.1.2 词嵌入主要主题内容
  1. 什么是词嵌入

  2. 不同类型的词嵌入

    • 基于频率的词嵌入

      • Count Vector
      • TF-IDF
      • Co-Occurrence Matrix
    • 基于预测的词嵌入

      • CBOW
      • Skip-Gram
  3. 词嵌入使用示例

  4. 使用预训练的词向量

  5. 训练自己的词向量

3.2 Word Embedding–word2vec

3.2.1 word2vec 简介

word2vec 训练神经网络得到一个关于输入 X 和 输出 Y 之间的语言模型,我们的关注重点并不是说要把这个模型训练的有多好, 而是要获取训练好的神经网络权重,这个权重就是我们要拿来对输入词汇 X 的向量化表示。一旦我们拿到了训练语料所有词汇的词向量, 接下来开展 NLP 研究工作就相对容易一些了。

word2vec 通常有两个版本的语言模型:

  • 一种是给定上下文词,需要我们来预测中间目标词,这种模型叫做连续词袋模型(Continuous Bag-of-Wods Model,CBOW)
  • 另一种是给定一个词语,我们来根据这个词预测它的上下文,这种模型叫做 skip-gram 模型。而且每个模型都有两种策略

CBOW 和 Skip-gram 模型的原理示意图:

_images/CBOW_Skip_gram.png
3.2.2 NNLM

2003 年提出了神经网络语言模型(Neural Network Language Model, NNLM), 其用前 \(n-1\) 个词预测第 \(n\) 个词的概率,并用神经网络搭建模型。

  • NNLM 模型的基本结构

    _images/NNLM.png
  • NNLM 模型目标函数

    \[L(\theta) = \sum_{t} log P(\omega_{t}|\omega_{t-n}, \omega_{t-n+1}, \cdots, \omega_{t-1})\]
    • 使用非对称的前向窗口,长度为 \(n-1\) ,滑动窗口遍历整个语料库求和,使得目标概率最大化,其计算量正比于语料库的大小。 同时,预测所有词的概率综合应为 1。
    \[\sum_{\omega \in \{vocabulary\}} P(\omega|\omega_{t-n+1}, \cdots, \omega_{t-1})\]
  • NNLM 模型解释

    • 样本的一组输入是第 \(n\) 个词的前 \(n-1\) 个词的 One-Hot 表示,目标是预测第 \(n\) 个词, 输出层的大小是语料库中所有词的数量,然后 sotfmax 回归,使用反向传播不断修正神经网络的权重来最大化第 \(n\) 个词的概率。 当神经网络学得到权重能够很好地预测第 \(n\) 个词的时候,输入层到映射层,即这层,其中的权重 Matrix C 被称为投影矩阵, 输入层各个词的 Ont-Hot 表示法只在其对应的索引位置为 1,其他全为 0,在与 Matrix C 矩阵相乘时相当于在对应列取出列向量投影到映射层。

      \[\begin{split}Matrix C = (w_{1}, w_{2}, \cdots, w_{v}) = \begin{bmatrix} (\omega_{1})_{1} & (\omega_{2})_{1} & \cdots & (\omega_{v})_{1} \\ (\omega_{1})_{2} & (\omega_{2})_{2} & \cdots & (\omega_{v})_{2} \\ \vdots & \vdots & & \vdots \\ (\omega_{1})_{D} & (\omega_{2})_{D} & \cdots & (\omega_{v})_{D} \\ \end{bmatrix}\end{split}\]

      此时的向量​就是原词​的分布式表示,其是稠密向量而非原来 One-Hot 的稀疏向量了。

      在后面的隐藏层将这 n-1 个稠密的词向量进行拼接,如果每个词向量的维度为 D,则隐藏层的神经元个数为 (n-1)×D, 然后接一个所有待预测词数量的全连接层,最后用 softmax 进行预测。

      可以看到,在隐藏层和分类层中间的计算量应该是很大的,word2vec 算法从这个角度出发对模型进行了简化。 word2vec 不是单一的算法,而是两种算法的结合:连续词袋模型(CBOW)和跳字模型(Skip-gram)。

3.2.3 CBOW

CBOW 模型的应用场景是要根据上下文预测中间的词,所以输入便是上下文词,当然原始的单词是无法作为输入的, 这里的输入仍然是每个词汇的 One-Hot 向量,输出 Y 为给定词汇表中每个词作为目标词的概率。

  • CBOW 模型基本结构(Rong Xin)

    _images/CBOW.png
  • CBOW 模型解释

    • 可见 CBOW 模型结构是一种普通的神经网络结构。主要包括输入层、中间隐藏层、最后的输出层。 以输入、输出样本 \((Context(w), w)\) 为例对 CBOW 模型的三个网络层进行简单说明, 其中假设 \(Context(w)\)\(w\) 前后各 \(c\) 个词构成。数学细节如下:

      • 输入层: 包含 \(Context(w)\) 中 2c 个词的词向量 \(v(Context(w)_{1}), v(Context(w)_{2}), \cdots, v(Context(w)_{2c}) \in R^{m}\)。 这里, \(m\) 的含义同上表示词向量的长度;
      • 投影层: 将输入层的 \(2c\) 个向量做求和累加,即 \(x_{w}=\sum_{i=1}^{2c}v(Context(w)_{i}) \in R^{m}\).
      • 输出层: 输出层对应一颗二叉树,它是以语料中出现过的词当叶子节点,以各词在语料中出现的次数当权值构造出来的 Huffman 树。在这棵 Huffman 树中, 叶子节点共 \(N(=|D|)\) 个,分别对应词典 \(D\) 中的词,非叶子节点 \(N-1\) 个(图中标成黄色的那些节点)。
    • 普通的基于神经网络的语言模型输出层一般就是利用 softmax 函数进行归一化计算,这种直接 softmax 的做法主要问题在于计算速度, 尤其是我们采用了一个较大的词汇表的时候,对大的词汇表做求和运算,softmax 的分母运算会非常慢,直接影响到了模型性能。

    • 可以看到,上面提到的取消隐藏层,投影层求和平均都可以一定程度上减少计算量,但输出层的数量在那里, 比如语料库有 500W 个词,那么隐藏层就要对 500W 个神经元进行全连接计算,这依然需要庞大的计算量。 word2vec 算法又在这里进行了训练优化.

    • 除了分级 softmax 输出之外,还有一种叫做负采样的训练 trick

    • CBOW 目标函数

      \[J = \sum_{\omega \in corpus} P(\omega | context(\omega))\]
  • CBOW 在 NNLM 基础上有以下几点创新

    • 1.取消了隐藏层,减少了计算量
    • 2.采用上下文划窗而不是前文划窗,即用上下文的词来预测当前词
    • 3.投影层不再使用各向量拼接的方式,而是简单的求和平均
  • 参考资料:

3.2.4 Skip-gram

Skip-gram 模型的应用场景是要根据中间词预测上下文词,所以输入 \(X\) 是任意单词, 输出 \(Y\) 为给定词汇表中每个词作为上下文词的概率。

  • Skip-gram 模型基本结构

    _images/Skip-gram2.png _images/Skip-gram.png
  • Skip-gram 模型解释

    • 从上面的结构图可见,Skip-gram 模型与 CBOW 模型翻转,也是也是一种普通的神经网络结构, 同样也包括输入层、中间隐藏层和最后的输出层。继续以输入输出样本 \((Context(w),w)\) 为例 对 Skip-gram 模型的三个网络层进行简单说明, 其中假设 \(Context(w)\)\(w\) 前后各 \(c\) 个词构成。数学细节如下:

      • 输入层: 只含当前样本的中心词 \(w\) 的词向量 \(v(w) \in R^{m}\)
      • 投影层: 这是个恒等投影,把 \(v(w)\) 投影到 \(v(w)\),因此,这个投影层其实是多余的。 这里之所以保留投影层主要是方便和 CBOW 模型的网络结构做对比
      • 输出层: 和 CBOW 模型一样,输出层也是一棵 Huffman 树
    • Skip-gram 模型的训练方法也是基于损失函数的梯度计算,目标函数:

      \[\begin{split}L = \sum_{w \in C} log \prod_{u \in Context(w)} \prod_{j=2}^{l^{u}}\{[\sigma(v(w)^{T}\theta_{j-1}^{u})]^{1-d_{j}^{u}} \cdot [1-\sigma(v(w)^{T}\theta_{j-1}^{u})]^{d_{j}^{u}} \} \\ = \sum_{w \in C} \sum_{u \in Context(w)} \sum_{j=2}^{l^{u}} {(1-d_{j}^{u}) \cdot log[\sigma(v(w)^{T}\theta_{j-1}^{u})] + d_{j}^{u} \cdot log[1 - \sigma(v(w)^{T}\theta_{u}^{j-1})]}\end{split}\]

Note

关于 CBOW 和 skip-gram 模型的更多数学细节,比如 Huffman 树、损失函数的推导等问题, 从监督学习的角度来说,word2vec 本质上是一个基于神经网络的多分类问题,当输出词语非常多时, 我们则需要一些像分级 Softmax 和负采样之类的 trick 来加速训练。但从自然语言处理的角度来说, word2vec 关注的并不是神经网络模型本身,而是训练之后得到的词汇的向量化表征。 这种表征使得最后的词向量维度要远远小于词汇表大小,所以 word2vec 从本质上来说是一种降维操作。 我们把数以万计的词汇从高维空间中降维到低维空间中,大大方便了后续的 NLP 分析任务。

3.2.5 Google word2vec 工具
3.2.5.1 word2vec 工具简介
  • word2vec 是 Google 在 2013 年发布的一个开源词向量建模工具
  • word2vec 使用的算法是 Bengio 等人在 2001 年提出的 Neural Network Language Model(NNLM) 算法
  • word2vec 是一款将词表征为实数值向量的高效工具
3.2.5.2 word2vec 核心思想
  • word2vec 以及其他词向量模型,都基于同样的假设:

      1. 衡量词语之间的相似性,在于相邻词汇是否相识,这是基于语言学的“距离象似性”原理。
      1. 词汇和它的上下文构成了一个象,当从语料库当中学习得到相识或者相近的象时,它们在语义上总是相识的。
3.2.5.3 word2vec 模型
  • CBOW(Continuous Bag-Of-Words, 连续的词袋模型)
  • Skip-Gram
3.2.5.4 word2vec 优点
  • 高效,Mikolov 在论文中指出一个优化的单机版本一天可以训练上千亿个词
3.2.5.5 训练 word2vec

1.word2vec 版本

2.Gensim word2vec 示例

使用中文维基百科语料库作为训练库

  1. 数据预处理

    • 大概等待 15min 左右,得到 280819 行文本,每行对应一个网页
    from gensim.corpora import WikiCorpus
    
    space = " "
    with open("wiki-zh-article.txt", "w", encoding = "utf8") as f:
          wiki = WikiCorpus("zhwiki-latest-pages-articles.xml.bz2", lemmatize = False, dictionary = {})
          for text in wiki.get_texts():
             f.write(space.join(text) + "\n")
    print("Finished Saved.")
    
  2. 繁体字处理

    • 目的:

      • 因为维基语料库里面包含了繁体字和简体字,为了不影响后续分词,所以统一转化为简体字
    • 工具

opencc -i corpus.txt -o wiki-corpus.txt -c t2s.json
  1. 分词
    • jieba
    • ICTCLAS(中科院)
    • FudanNLP(复旦)

Note

3.2.6 训练一个 word2vec 词向量

通常而言,训练一个词向量是一件非常昂贵的事情,我们一般会使用一些别人训练好的词向量模型来直接使用, 很少情况下需要自己训练词向量,但这并不妨碍我们尝试来训练一个 word2vec 词向量模型进行试验。

  • 如何训练一个 skip-gram 模型,总体流程是

    • (1)先下载要训练的文本语料
    • (2)然后根据语料构造词汇表
    • (3)再根据词汇表和 skip-gram 模型特点生成 skip-gram 训练样本
    • (4)训练样本准备好之后即可定义 skip-gram 模型网络结构,损失函数和优化计算过程
    • (5)最后保存训练好的词向量即可
import collections
import math

3.3 Word Embedding–para2vec

  • 段落到向量

3.4 Word Embedding–doc2vec

  • 文章到向量

3.5 Word Embedding–GloVe

除了 word2vec 之外,常用的通过训练神经网络的方法得到词向量的方法还包括 Glove(Global Vectors for Word Representation)词向量、 fasttext 词向量等等。本节笔者将对 GloVe 词向量进行简单的介绍, 并在此基础上对基于 GloVe 模型训练好的词向量计算预先相似度和语义近似与类比等分析。

  • 通过余弦函数、欧几里得距离来获得相似词的库
3.5.1 GloVe 词向量简介

GloVe 词向量直译为全局的词向量表示,跟 word2vec 词向量一样本质上是基于词共现矩阵来进行处理的。 GloVe 词向量模型基本步骤如下:

  • 1.基于词共现矩阵收集词共现信息

    • 假设 \(X_{ij}\) 表示词汇 \(i\) 出现在词汇 \(j\) 上下文的概率,首先对语料库进行扫描, 对于每个词汇,我们定义一个 window_size,即每个单词向两边能够联系到的距离,在一句话中如果一个词距离中心词越远, 我们给与这个词的权重越低.
  • 2.对于每一组词,都有

    \[\omega_{i}^{T}\omega_{j} + b_{i} + b_{j} = log(X_{ij})\]
    • 其中, \(\omega_{i}\) 表示中心词向量, \(\omega_{j}\) 表示上下文词向量, \(b_{i}\)\(b_{j}\) 均表示上下文词的常数偏倚.
  • 3.定义 GloVe 模型损失函数

    \[J = \sum_{i=1}^{V}\sum_{j=1}^{V}f(X_{ij})(\omega_{i}^{T}\omega_{j} + b_{i} + b_{j} - log X_{ij})^{2}\]
    • 其中,加权函数 \(f\) 可以帮助我们避免只学习到一个常见词的词向量模型, \(f\) 函数的选择原则在于既不给常见词(this/of/and)以过分的权重, 也不回给一些不常见词(durion)太小的权重,参考形式如下:

      \[\begin{split}\begin{split} f(X_{ij})= \left \{ \begin{array}{rcl} (\frac{X_{ij}}{x_{max}})^{\alpha}, & & {如果 X_{ij} < x_{max}} \\ 1, & & {否则} \\ \end{array} \right. \end{split}\end{split}\]
  • 4.计算余弦相似度

    • 为了衡量两个单词在语义上的相近性,我们采用余弦相似度来进行度量。余弦相似度的计算公式如下:

      \[CosineSimilarity(u, v)=\frac{uv}{||u||_{2} ||v||_{2}}=cos(\theta)\]
    • 基于余弦相似度的词汇语义相似性度量:

      _images/cosine_similarity.png
  • 5.语义类比

    • 有了词汇之间的相似性度量之后,便可以基于此做进一步分析,比如要解决 a is to b as c is to _ 这样的语义填空题, 可以利用词汇之间的余弦相似性计算空格处到底填什么单词.
3.5.2 Glove vs word2vec
_images/GloVe_vs_word2vec.png

3.6 Word Embedding 模型

3.6.1 层级 Softmax
  • 霍夫曼树

    霍夫曼树是一棵特殊的二叉树,了解霍夫曼树之前先给出几个定义:

    • 路径长度:在二叉树路径上的分支数目,其等于路径上结点数-1
    • 结点的权:给树的每个结点赋予一个非负的值
    • 结点的带权路径长度:根结点到该结点之间的路径长度与该节点权的乘积
    • 树的带权路径长度:所有叶子节点的带权路径长度之和

    霍夫曼树的定义为:

    • 在权为 \(\omega_{1}, \omega_{2}, \cdots, \omega_{n}\) ​的​ \(n\) 个叶子结点所构成的所有二叉树中, 带权路径长度最小的二叉树称为最优二叉树或霍夫曼树

    可以看出,结点的权越小,其离树的根结点越远。

  • 层级 Softmax

    word2vec 算法利用霍夫曼树,将平铺型 softmax 压缩成层级 softmax,不再使用全连接。 具体做法是根据文本的词频统计,将词频赋给结点的权。

    在霍夫曼树中,叶子结点是待预测的所有词,在每个子结点处,用 sigmoid 激活后得到往左走的概率 p, 往右走的概率为 1-p。最终训练的目标是最大化叶子结点处预测词的概率。

    层级 softmax 的实现有点复杂,暂时先搞清楚大致原理~

3.6.2 负例采样(Negative Sampling)

负例采样的想法比较简单,假如有 \(m\) 个待预测的词,每次预测的一个正样本词,其他的 \(m-1\) 个词均为负样本。

  • 一方面正负样本数差别太大
  • 另一方面,负样本中可能有很多不常用,或者词预测时概率基本为0的样本,我们不想在计算它们的概率上面消耗资源

比如现在待预测的词有 100W 个,正常情况下,我们分类的全连接层需要 100W 个神经元,我们可以根据词语的出现频率进行负例采样, 一个正样本加上采样出的比如说 999 个负样本,组成 1000 个新的分类全连接层。

采样尽量保持了跟原来一样的分布,具体做法是将 \([0, 1]\) 区间均分为 108 份,然后根据词出现在语料库中的次数赋予每个词不同的份额。

\[len(\omega) = \frac{counter(\omega)}{\sum_{\mu \in D} counter(\mu)}\]

然后在 \([0, 1]\) 区间掷筛子,落在哪个区间就采样哪个样本。实际上,最终效果证明上式中取 \(counter(\omega)\)\(3/4\) 次方效果最好, 所以在应用汇总也是这么做的。

3.6.3 Fasttext
3.6.3.1 fasttext 算法简介

fasttext 的模型与 CBOW 类似,实际上,fasttext 的确是由 CBOW 演变而来的。CBOW 预测上下文的中间词,fasttext 预测文本标签。 与 word2vec 算法的衍生物相同,稠密词向量也是训练神经网路的过程中得到的。

_images/fasttext.png

fasttext 的输入是一段词的序列,即一篇文章或一句话,输出是这段词序列属于某个类别的概率,所以,fasttext 是用来做文本分类任务的。

fasttext 中采用层级 softmax 做分类,这与 CBOW 相同。fasttext 算法中还考虑了词的顺序问题,即采用 N-gram, 与之前介绍的离散表示法相同,如:

  • 今天天气非常不错,Bi-gram 的表示就是:今天、天天、天气、气非、非常、常不、不错

fasttext 做文本分类对文本的存储方式有要求:

其中:

  • __label__:为实际类别的前缀,也可以自己定义
3.6.3.2 fasttext 的 Python 实现
  • GitHub:

  • 示例:

    classifier = fasttext.supervised(input_file, output, label_prefix = "__label__")
    result = classifier.test(test_file)
    print(result.precision, result.recall)
    

    其中:

    • input_file:是已经按照上面的格式要求做好的训练集 txt
    • output:后缀为 .model,是保存的二进制文件
    • label_prefix:可以自定类别前缀

NLP–指代消解

1.test

2.test

NLP-情感分析

1.情感分析简介

  • 情感分析介绍

    随着互联网的飞速发展,越来越多的互联网用户从单纯的信息受众, 变为互联网信息制造的参与者。互联网中的博客、微博、论坛、 评论等这些主观性文本可以是用户对某个产品或服务的评价, 或者是公众对某个新闻事件或者政策的观点。

    潜在的消费者在购买某个产品或者服务时获取相关的评论可以作为决策参考, 政府部门也可以浏览公众对新闻事件或政策的看法了解舆情。 这些主观性的文本对于决策者制定商业策略或者决策都非常重要, 而以往仅靠人工监控分析的方式不仅耗费大量人工成本,而且有很强的滞后性。 因此采用计算机自动化情感分析称为目前学术界和工业界的大趋势。 目前,情感分析在实际生产场景中得到越来越多的应用。

  • 情感分析技术要点

    • 舆情数据舆情分析
    • 分类算法
    • RNN
    • LSTM

2.情感分析应用

  • (1)电子商务
    • 情感分析最常用的领域就是电子商务。例如淘宝、京东,用户在购买一件商品以后可以发表他们关于该商品的体验。 通过分配登记或者分数,这些网站能够为产品和产品的不同功能提供简要的描述。 客户可以很容易产生关于整个产品的一些建议和反馈。通过分析用户的评价, 可以帮助这些网站提高用户满意度,完善不到位的地方。
  • (2)舆情分析
    • 无论是政府还是公司,都需要不断监控社会对于自身的舆论态度。来自消费者或者任何第三方机构的正面或者负面的新闻报道, 都会影响到公司的发展。相对于消费者,公司更看重品牌声誉管理(BRM)。如今,由于互联网的放大效应, 任何一件小的事情都可能发酵为大的舆论风暴,及时感知舆情,进行情感分析有助于及时公关, 正确维护好公司的品牌,以及产品和服务评价。
  • (3)市场呼声
    • 市场呼声是指消费者使用竞争对手提供的产品与服务的感受。及时准确的市场呼声有助于取得竞争优势, 并促进新产品的开发。尽早检测这类信息有助于进行直接、关键的营销活动。 情感分析能够为企业实时获取消费者的意见。这种实时的信息有助于企业制定新的营销策略,改进产品功能, 并预测产品故障的可能。
  • (4)消费者呼声
    • 消费者呼声是指个体消费者对产品或服务的评价。这就需要对消费者的评价和反馈进行分析。 VOC是指客户体验管理中的关键要素。VOC 有助于企业抓住产品开发的新机会。 提取客户意见同样也能帮助企业确定产品的功能需求和一些关于性能、成本的肺功能需求。

3.情感分析的基本方法

根据分析载体的不同,情感分析会涉及到很多主题,包括电影评论、商品评论,以及新闻和博客等的情感分析。 大多数情感分析研究都使用机器学习方法。

  • 对情感分析的研究到目前为止主要集中在两个方面:

    • 识别给定的文本实体是主观的还是客观的
    • 识别主观的文本的极性
      • 文本可以划分为积极和消极两类,
      • 或者积极、消极、中性(或者不相关)的多类
  • 情感分析的方法主要分为:

    • 词法分析
    • 基于机器学习的分析
    • 混合分析

3.1 词法分析

词法分析运用了由预标记词汇组成的字典,使用词法分析器将输入文本转换为单词序列。 将每一个新的单词与字典中的词汇进行匹配。如果有一个积极的匹配,分数加到输入文本的分数总池中。 相反,如果有一个消极的匹配,输入文本的总分会减少。虽然这项技术感觉有些业余,但已被证明是有价值的。 词法分析技术的工作方式如下图:

_images/word_analysis.png

文本的分类取决于文本的总得分。目前有大量的工作致力于度量词法信息的有效性。对于单个短语, 通过手动标记词汇(仅包含形容词)的方式,大概能达到 85% 的准确性。这是由评价文本的主观性所决定的。

词法分析也存在一个不足:其性能(时间复杂度和准确率)会随着字典大小(词汇的数量)的增加而迅速下降。

3.2 机器学习方法

在情感分析中,主要使用的是监督学习方法。在训练过程中,需要提供一个标记语料库作为训练数据。 分类器使用一系列特征向量对目标数据进行分类。

通常来说,unigram(单个短语)、bigrams(两个连续的短语)、trigrams(三个连续的短语)都可以被选为特征向量。 当然还有其他一些特征,如积极词汇的数量、消极词汇的数量、文档的长度等。

支持向量机(SVM)、朴素贝叶斯(NB)算法、卷积神经网络(CNN)等都可以作为分类器使用。

机器学习技术面临很多挑战:分类器的设计、训练数据的获取、对一些未见过的短语的正确解释。 相比词汇分析方法,它在字典大小呈指数增长的时候依然工作得很好。

3.3 混合分析

情感分析研究的进步吸引大量研究者开始探讨将两中方法进行组合的可能性,即可以利用机器学习方法的高准确性, 又可以利用词法分析快速的特点。

  • 有研究者利用由两个词组成的词汇和一个未标记的数据,将这些由两个词组成的词汇划分为积极的类和消极的类。 利用被选择的词汇集合中的所有单词产生一些伪文件。然后计算伪文件与未标记文件之间的余弦相似度。 根据相似度将该文件划分为积极的或消极的情感。之后这些训练数据集被送入朴素贝叶斯分类器进行训练。
  • 有研究者使用背景词法信息作为单词类关联,提出了一种统一的框架,设计了一个 Polling 多项式分类器(PMC, 多项式朴素贝叶斯), 在训练中融入了手动标记数据,利用词法之后后性能得到了提高。

4.情感分析示例

  • 在 NLP 中,情感分析是 一段文字 表达的 情绪状态

    • 一段文本[输入]:可以是一个句子、一个段落、一个文档
    • 情感状态[输出]:可以是两类,也可以是多类
  • 在 NLP 问题中,情感分析可以被归类为文本分类问题,主要涉及两个问题:

    • 文本表达(特征提取)
      • BOW (词袋模型)
      • topic model(主题模型)
      • word2vec
    • 文本分类
      • SVM
      • NB
      • LR
      • CNN
      • RNN
      • LSTM
  • 情感分析的任务是分析一句话是积极、消极还是中性的,可以把任务分为 5 个部分:

    • (1)训练或者载入一个词向量生成模型
    • (2)创建一个用于训练集的 ID 矩阵
    • (3)创建 LSTM 计算单元
    • (4)训练分类模型
    • (5)测试分类模型

4.1 载入数据

  • IMDB 情感分析数据
    • 25000 条已标注的电影评价,满分 10 分
    • 25000 条已标注的电影评分,满分 10 分
  • 评价标签阈值表

    标签 分数
    负面评价 \(\leq 4\)
    正面评价 \(\geq 7\)
  • 示例:

    • 数据加载

      # -*- coding: utf-8 -*-
      
      
      import os
      from os.path import isfile, join
      import re
      import numpy as np
      from random import randint
      import tensorflow as tf
      import config
      
      
      batch_size = 24
      lstm_units = 64
      num_labels = 2
      iterations = 100
      lr = 0.001
      
      
      def load_wordsList():
         """
         载入词典,该词典包含 400000 个词汇
         """
         wordsList = np.load(os.path.join(config.root_dir, 'wordsList.npy'))
         print("-" * 20)
         print('载入word列表...')
         print("-" * 20)
         wordsList = wordsList.tolist()
         wordsList = [word.decode('UTF-8') for word in wordsList]
      
         return wordsList
      
      
      def load_wordVectors():
         """
         载入已经训练好的词典向量模型,该矩阵包含了的文本向量,维度: (400000, 50)
         """
         wordVectors = np.load(os.path.join(config.root_dir, 'wordVectors.npy'))
         print("-" * 20)
         print('载入文本向量...')
         print("-" * 20)
      
         return wordVectors
      
      
      def load_idsMatrix():
         """
      
         """
         ids = np.load(os.path.join(config.root_dir, 'idsMatrix.npy'))
      
         return ids
      
      
      def postive_analysis():
         """
         载入正面数据集
         """
         pos_files = [config.pos_data_dir + f for f in os.listdir(config.pos_data_dir) if isfile(join(config.pos_data_dir, f))]
         num_words = []
         for pf in pos_files:
            with open(pf, "r", encoding='utf-8') as f:
                  line = f.readline()
                  counter = len(line.split())
                  num_words.append(counter)
         print("-" * 20)
         print('正面评价数据加载完结...')
         print("-" * 20)
         num_files = len(num_words)
         print('正面评价数据文件总数', num_files)
         print('正面评价数据所有的词的数量', sum(num_words))
         print('正面评价数据平均文件词的长度', sum(num_words) / len(num_words))
      
         return pos_files
      
      
      def negtive_analysis():
         """
         载入负面数据集
         """
         neg_files = [config.neg_data_dir + f for f in os.listdir(config.neg_data_dir) if isfile(join(config.neg_data_dir, f))]
         num_words = []
         for nf in neg_files:
            with open(nf, "r", encoding='utf-8') as f:
                  line = f.readline()
                  counter = len(line.split())
                  num_words.append(counter)
         print("-" * 20)
         print('负面评价数据加载完结...')
         print("-" * 20)
         num_files = len(num_words)
         print('负面评价数据文件总数', num_files)
         print('负面评价数据所有的词的数量', sum(num_words))
         print('负面评价数据平均文件词的长度', sum(num_words) / len(num_words))
      
         return neg_files
      
      
      if __name__ == "__main__":
         # 词典
         wordsList = load_wordsList()
         print("词典中词汇数量:", len(wordsList))
         home_index = wordsList.index("home")
         print("'home' 单词在词典中的索引:", home_index)
      
         # 词典向量模型矩阵
         wordVectors = load_wordVectors()
         print("词典向量模型矩阵:", wordVectors.shape)
         print("'home' 在词典向量模型矩阵中的向量表示:", wordVectors[home_index])
      
         # 正面、负面文本数据
         pos_files = postive_analysis()
         neg_files = negtive_analysis()
      
    • 数据可视化

      def data_visual(num_words):
         import matplotlib as mpl
         import matplotlib.pyplot as plt
         # mpl.use("qt4agg")
         # 指定默认字体
         mpl.rcParams["font.sans-serif"] = ["SimHei"]
         mpl.rcParams["font.family"] = "sans-serif"
         # %matplotlib inline
         plt.hist(num_words, 50, facecolor = "g")
         plt.xlabel("文本长度")
         plt.ylabel("频次")
         plt.axis([0, 1200, 0, 8000])
         plt.show()
      
      if __name__ == "__main__":
         # 正面、负面文本数据
         pos_files, pos_num_words = postive_analysis()
         neg_files, neg_num_words = negtive_analysis()
      
         # 文本数据可视化
         num_total_words = pos_num_words + neg_num_words
         data_visual(num_total_words)
      
      _images/pos_neg_data.png

4.2 辅助函数

4.3 模型设置

4.4 调参配置

  • 学习率
    • RNN 网络最困难的部分就是它的训练速度慢,耗时非常久,所以学习率至关重要。如果学习率设置过大, 则学习曲线会有很大的波动性,如果设置过小,则收敛得非常慢。根据经验设置 0.001 比较好。 如果训练得非常慢,可以适当增大这个值。
  • 优化器
    • Adam 广泛被使用
  • LSTM 单元数量
    • 取决于输入文本的平均长度,单元数量过多会导致速度非常慢
  • 词向量维度
    • 词向量一般设置在 50~300 之间,维度越多可以存放越多的单词信息,但是也意味着更高的计算成本

4.5 训练模型

$ tensorflow --logdir=tensorboard

NLP–纠错

1.test

2.test

NLP-问答系统

1.NLP 问答系统简介

2.基于关键词匹配的 NLP 问答系统

_images/QA_1.png
  • 基于 FEMA 表抽取实体、关系
  • 基于 Neo4j 图数据库存储
  • 基于 java SpringBoot 框架做后端接口
  • 基于 HanLP 进行实体识别
  • 根据用户输入的问题,进行设备实体、失效模式实体识别,名词为设备实体,动词为失效模式实体
  • 通过 Cypher 语句在 Neo4j 中查询设备实体匹配数据库中的设备、组件等实体,失效模式实体匹配数据库中的失效模式,
  • 匹配的方式是模糊查询。返回整个节点路径,包括:设备 -> 一级组件 -> 二级组件 -> 三级组件 -> 失效模式 -> 根本原因 -> 措施

3.基于相似度匹配的 NLP 问答系统

_images/QA_2.png
  • 基于问答对形式的数据

  • 基于向量数据库 milvus 和关系型数据库 PostgreSQL

    • milvus 存储问题向量
    • PostgreSQL 存储问题答案
  • 基于 Python fastapi 做后端接口

  • 基于 bert-as-service 进行文本的 embedding 向量化

  • 用户输入问题,首先将问题向量化,然后使用 milvus 提供的 search 函数,查询 milvus 数据库中的 top_k 个与该向量最为相似的答案,返回 ID, 然后根据 ID 到 PostgreSQL 查询问题答案

NLP–机器学习

  • 文本分类
    • 朴素贝叶斯
    • 逻辑回归
    • 支持向量机
  • 文本聚类
    • K-means
  • 特征提取
    • Bag-of-words
    • TF
    • IDF
    • TF-IDF
    • N-Gram
  • 标注
  • 搜索与排序
  • 推荐系统
  • 序列学习
    • 语音识别
    • 文本转语音
    • 机器翻译

1.test

2.test

NLP–文本分类

1.文本分类任务简介

一般,文本分类大致分为以下几个步骤:

  • 1.定义阶段
    • 定义数据以及分类体系,具体分为哪些类别,需要哪些数据
  • 2.数据预处理
    • 对文档做分词、去停用词等准备工作
  • 3.数据提取特征
    • 对文档矩阵进行降维,提取训练集中最有用的特征
  • 4.模型训练阶段
    • 选择具体分类模型以及算法,训练出文本分类器
  • 5.评测阶段
    • 在测试集上测试并评价分类器性能
  • 6.应用阶段
    • 应用性能最高的分类模型对待分类文档进行分类

2.朴素贝叶斯分类模型

2.1 理论

2.2 实现

2.使用 fastText 构建用户评论模型

  • 构建用户评论模型
  • 使用 Facebook 的 fastText,一个很强大的文本分类模型库

2.1 下载训练数据

  • Yelp 提供了一个拥有 470万 用户评论的研究数据集

NLP–文本聚类

1.无监督学习

无监督学习希望能够发现数据本身的规律和模式,与监督学习相比,无监督学习不需要对数据进行标记。这样可以解决大量人力、物力, 也可以让数据的获取变得非常容易。

某种程度上说,机器学习的终极目标就是无监督学习。从功能上看,无监督学习可以帮助我们发现数据的“簇”, 同时也可以帮助我们找寻“离群点(outlier)”。

此外,对于特征维度特别高的数据样本,我们同样可以通过无监督学习对数据进行降维, 保留数据的主要特征,这样对高维空间的数据也可以进行处理。

常见的非监督学习任务:

  • 聚类
    • 需要定义相似性
  • 子空间估计
    • 通常研究如何将原始数据向量在更低维度下表示
    • 理想情况下,子空间的表示要具有代表性从而才能与原始数据接近,最常用的方法叫做主成分分析
  • 表征学习
    • 希望在欧几里得空间中找到原始对象的表示方式,从而能在欧几里得空间里表示出原始对象的符号性质
    • 例如:希望找到城市的向量表示,从而使得可以进行这样的向量运算:首都 + 美国 = 华盛顿
  • 生成对抗网络
    • 描述数据的生成过程,并检查真实数据与生成数据是否统计上相似

2.文本聚类

聚类视图将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个“簇(cluser)”。 通过这样的划分,每个簇可能对应于一些潜在的类别。这些概念对聚类算法而言事先是未知的, 聚类过程仅能自动形成簇结构,簇所对应的含义需要由使用者来把握和命名。 聚类常用于寻找数据内在的分布结构,也可作为分类等其他学习任务的前驱过程。

  • 在 NLP 领域,一个很重要的应用方向是文本聚类,文本聚类有很多中算法,例如:K-means、DBScan、BIRCH、CURE 等。

  • 文本聚类存在大量的使用场景,比如数据挖掘、信息检索、主题检测、文本概括。

  • 聚类算法中初始的聚类点对后续的最终划分有非常大的影响,选择合适的初始点,可以加快算法的收敛速度和增强类之间的区分度。 选择初始聚类点的方法有如下几种:

    • 随机选择法
      • 随机的选择 k 个对象作为初始聚类点
    • 最小最大法
      • 先选择所有对象中的相距最遥远的两个对象作为聚类点。然后选择第三个点,使得它与确定的聚类点的最小距离是所有点中最大的, 然后按照相同的原则选取
    • 最小距离法
      • 选择一个正数 r,把所有对象的中心作为第一个聚类点,然后依次输入对象,当前输入对象与确认的聚点的距离都大于 r 时, 则该对象作为新的聚类点
    • 最近归类法
      • 划分方法就是决定当前对象应该分到哪个簇中。
      • 划分方法中最为流行的是最近归类法,即将当前对象归类与最近的聚类点

NLP–BERT

1.BERT 介绍

1.1 BERT 简介

  • test

1.2 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

2.BERT 预训练模型库

3.bert-serving-server 搭建 BERT 词向量服务

3.1 bert-serving-server 简介

  • BERT 工具能够快速得到词向量表示,叫做: bert-as-service,只要调用该服务就能够得到想要的向量表示

3.2 安装 bert-as-service

3.2.1 环境要求
  • Python>=3.5
  • TensorFlow>=1.10
3.2.2 安装服务端和客户端
$ pip install -i https://pypi.tuna.tsinghua.edu.cn/simple bert-serving-server bert-serving-client

3.3 启动 BERT 服务

3.3.1 下载预训练模型
  • GitHub 地址

    • BERT-Large, Uncased (Whole Word Masking):
      • 24-layer, 1024-hidden, 16-heads, 340M parameters
    • BERT-Large, Cased (Whole Word Masking):
      • 24-layer, 1024-hidden, 16-heads, 340M parameters
    • BERT-Base, Uncased:
      • 12-layer, 768-hidden, 12-heads, 110M parameters
    • BERT-Large, Uncased:
      • 24-layer, 1024-hidden, 16-heads, 340M parameters
    • BERT-Base, Cased:
      • 12-layer, 768-hidden, 12-heads , 110M parameters
    • BERT-Large, Cased:
      • 24-layer, 1024-hidden, 16-heads, 340M parameters
    • BERT-Base, Multilingual Cased (New, recommended):
      • 104 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
    • BERT-Base, Multilingual Uncased (Orig, not recommended) (Not recommended, use Multilingual Casedinstead):
      • 102 languages, 12-layer, 768-hidden, 12-heads, 110M parameters
    • BERT-Base, Chinese: Chinese Simplified and Traditional
      • 12-layer, 768-hidden, 12-heads, 110M parameters
  • 下载 BERT-Base, Chinese,放在项目根目录下

3.3.2 启动服务

解压缩后,运行如下命令进行启动,目录换成解压后的路径

$ bert-serving-start -model_dir /path/to/model -num_worker=4
$ bert-serving-start -model_dir /Users/zfwang/project/machinelearning/deeplearning/datasets/NLP_data/chinese_L-12_H-768_A-12 -num_worker=4

Note

  • -num_worker: 指定使用多少个 CPU
3.3.3 调用 BERT 进行应用
from bert_serving.client import BertClient
bc = BertClient(ip = "localhost", check_version = False, check_length = False)
vec = bc.encode(["学习"])
print(vec)

NLP–Attention

1.test1

2.test2

NLP–Transformer

1.Transformer 简介

2.

NLP–知识图谱

1.知识图谱简介

_images/knowledge_graph.png
  • 知识图谱定义

    • 知识图谱(Knowledge Graph),在图书情报界称为 知识域可视化知识领域映射地图, 是 显示知识发展进程与结构关系的一系列各种不同的图形,用可视化技术描述知识资源及其载体, 挖掘、分析、构建、绘制和显示知识及它们之间的相互联系。
    • 知识图谱是通过将应用数学、图形学、信息可视化技术、信息科学等学科的理论与方法与计量学引文分析、 共现分析等方法结合,并利用可视化的图谱形象地展示学科的核心结构、发展历史、 前沿领域以及整体知 识架构达到多学科融合目的的现代理论。它能为学科研究提供切实的、有价值的参考。
    • 简单说来,知识图谱就是通过不同知识的关联性形成一个网状的知识结构。
  • 知识图谱的应用

    • 知识图谱目前的应用主要在搜索、智能问答、推荐系统等方面
  • 知识图谱的建设

    • 知识图谱的建设,一般包括数据获取、实体识别和关系抽取、数据存储、图谱应用几个方面

2.知识图谱数据获取

3.知识图谱实体识别和关系抽取

4.知识图谱数据存储

  • 知识图谱由于其数据包含实体、属性、关系等,常见的关系型数据库诸如MySQL之类不能很好的体现数据的这些特点,因此知识图谱数据的存储一般是采用图数据库 (Graph Databases)。 而 Neo4j 是其中最为常见的图数据库。

5.知识图谱应用

NLP–Neo4j

1.Neo4j 介绍

1.1 Neo4j 是什么

  • Neo4j 是什么

    • Neo4j 是一个世界领先的开源图形数据库。它是由 Neo 技术使用 Java 语言完全开发的。

    • Neo4j是一个高性能的 NOSQL 图形数据库,它将结构化数据存储在网络上而不是表中。 它是一个嵌入式的、基于磁盘的、具备完全的事务特性的 Java 持久化引擎,但是它将 结构化数据存储在网络(从数学角度叫做图)上而不是表中。

    • Neo4j 也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。程序员 工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中——但是他们可以享受到 具备完全的事务特性、企业级的数据库的所有好处。

    • Neo4j 管网

  • Neo4j的特点

    • Neo4j 是开源的

    • Neo4j 无 Schema

    • Neo4j 的查询语言是 Neo4j CQL, 类似 SQL

    • Neo4j 是一种图形数据库

      • 图形数据库是以图形结构的形式存储数据的数据库。它以节点、关系、属性的形式存储应用程序的数据, 正如 RDBMS 以表的“行、列”的形式存储数据,GDBMS 以”图形”的形式存储数据。
    • Neo4j 遵循 属性图数据模型 存储和管理数据

      • 属性图模型规则如下:

        • 图形是 一组节点连接这些节点的关系

        • 节点关系 包含表示数据的 属性

          • 关系连接节点

            • 关系具有方向:单向、双向

              • 在属性图数据模型中,关系应该是定向的,如果尝试创建没有方向的关系,那么将抛出一个错误
              • 在 Neo4j 中,关系也应该是有方向性的,如果尝试创建没有方向的关系,那么将抛出一个错误
            • 每个关系包含:

              • 开始节点 或 从节点
              • 结束节点 或 到节点
          • 属性是用于表示数据的键值对

          • 节点用圆圈表示,关系用方向键表示

      • Neo4j 是一个流行的图数据库。其他图形数据库有:

        • Oracel NoSQL
        • OrientDB
        • HypherGraphDB
        • GraphBase
        • InfiniteGraph
        • AllegroGraph
    • Neo4j 通过使用 Apache Lucence 支持索引

    • Neo4j 支持 UNIQUE 约束

    • Neo4j 包含一个用于执行 CQL 命令的 UI:Neo4j 数据浏览器

    • Neo4j 支持完整的 ACID(原子性,一致性,隔离性和持久性)规则

    • Neo4j 采用原生图形库与本地 GPE(图形处理引擎)

    • Neo4j 支持查询的数据导出到 JSON 和 XLS 格式

    • Neo4j 提供了 REST API,可以被任何编程语言 (如Java,Spring,Scala等) 访问

    • Neo4j 提供了可以通过任何 UI MVC 框架 (如Node JS) 访问的 Java 脚本

    • Neo4j 支持两种 Java API: Cypher API 和 Native Java API 来开发 Java 应用程序

  • Neo4j的优点

    • 很容易表示连接的数据
    • 检索/遍历/导航更多的连接数据是非常容易和快速的
    • 非常容易地表示半结构化数据
    • Neo4j CQL 查询语言命令是人性化的可读格式,非常容易学习
    • 使用简单而强大的数据模型
    • 不需要复杂的连接来检索连接的/相关的数据,因为它很容易检索它的相邻节点或关系细节没有连接或索引
  • Neo4j 的缺点、限制

    • 节点数、关系、属性的限制
    • 不支持 Sharding
  • Neo4j 构建模块

    • 节点

    • 属性

    • 关系

    • 标签

      • Label将一个公共名称与一组节点或关系相关联。 节点或关系可以包含一个或多个标签。 我们可以为现有节点或关系创建新标签。 我们可以从现有节点或关系中删除现有标签。
    • 数据浏览器

1.2 Neo4j 概念详解

1.2.1 Neo4j 关系

Neo4j 图数据库遵循属性图模型来存储和管理其数据。根据属性图模型,关系应该是定向的, 否则,Neo4j 将抛出一个错误消息。基于方向性,Neo4j 关系被分为两种类型:

  • 单向关系
  • 双向关系

1.使用新节点创建关系

  • 语法

  • 示例

2.使用已知节点创建带属性的关系

  • 语法

  • 示例

3.检索关系节点的详细信息

  • 语法

  • 示例

4.示例

  • 目标:演示如何使用属性和创建两个节点、两个节点的关系

    • 需求: 创建两个节点:客户节点(Customer)和信用卡节点(CreditCard)

      • Customer 节点包含:id, name, dob 属性
      • CreditCard 节点包含:id, number, cvv, expiredate 属性
      • Customer 与 CreditCard 关系:DO_SHOPPING_WITH
      • CreditCard 到 Customer 关系:ASSOCIATED_WITH
    • 步骤:

      • 创建 Customer 节点
      • 创建 CreditCard 节点
      • 观察先前创建的两个节点: Customer 和 CreditCard
      • 创建 Customer 和 CreditCard 节点之间的关系
      • 查看新创建的关系详细信息
      • 详细查看每个节点和关系属性

1.3 Neo4j 安装、使用

1.3.1 Neo4j 安装
  • 首先在 https://neo4j.com/download/ 下载 Neo4j。Neo4j 分为社区版和企业版, 企业版在横向扩展、权限控制、运行性能、HA 等方面都比社区版好,适合正式的生产环境, 普通的学习和开发采用免费社区版就好。
  • 在 Mac 或者 Linux 中,安装好 jdk 后,直接解压下载好的 Neo4J 包, 运行 bin/neo4j start 即可
1.3.2 Neo4j 使用
  • Neo4j 提供了一个用户友好的 web 界面,可以进行各项配置、写入、查询等操作,并且提供了可视化功能。 类似 ElasticSearch 一样。
  • 打开浏览器,输入 http://127.0.0.1:7474/browser/,界面最上方就是交互的输入框。

2.Neo4j CQL

2.1 CQL 简介

  • Cypher 是 Neo4j 的声明式图形查询语言,允许用户不必编写图形结构的遍历代码, 就可以对图形数据进行高效的查询。Cypher 的设计目的类似 SQL,适合于开发者以 及在数据库上做点对点模式(ad-hoc)查询的专业操作人员。其具备的能力包括:

    • 创建、更新、删除节点和关系
    • 通过模式匹配来查询和修改节点和关系
    • 管理索引和约束等
  • Neo4j CQL 代表 Neo4j Cypher Query Language

    • CQL 是 Neo4j 图形数据库的查询语言
    • CQL 是一种声明性模式匹配语言
    • CQL 遵循 SQL 语法
    • CQL 的语法非常简单且人性化、可读性强
  • 类似 MySQL 一样,在实际的生产应用中,除了简单的查询操作会在 Neo4j 的 web 页面进行外, 一般还是使用 Python、Java 等的 driver 来在程序中实现。

2.2 CQL 命令关键字

CQL命令 用法
CREATE 创建节点、关系、属性
MATCH 检索节点、关系、属性数据
RETURN 返回查询结果
WHERE 提供条件过滤检索数据
DELETE 删除节点、关系
REMOVE 删除节点、关系的属性
ORDER BY 排序检索数据
SET 添加或更新标签

2.3 CQL 函数

定制列表功能 用法
String 用于使用 String 字面量
Aggregation 用于对 CQL 查询结果执行一些聚合操作
Relationshop 用于获取关系的细节,startnode, endnode等

2.4 CQL 数据类型

CQL 数据类型 用法
boolean 用于表示布尔文字: true, false
byte 用于表示8位整数
short 用于表示16位整数
int 用于表示32位整数
long 用于表示64位整数
float I用于表示32位浮点数
double 用于表示64位浮点数
char 用于表示16位字符
String 用于表示字符串

2.5 CQL 命令

2.5.1 CREATE

1.创建没有属性的节点

  • 创建 节点标签名称,相当于 MySQL 数据库中的表名

  • 语法

  • 说明

    • <node-name>: 要创建的节点名称
    • <node-label-name>: 要创建的节点标签
    • Neo4j 数据库服务器使用 <node-name> 将此节点详细信息存储在 Database.As 中作为 Neo4j DBA 或 Developer, 不能使用它来访问节点详细信息。
    • Neo4j 数据库服务器创建一个 <node-label-name> 作为内部节点名称的别名,作为 Neo4j DBA 或 Developer,应该使用此标签名称来访问节点详细信息。
  • 示例

2.创建具有属性的节点

  • 创建一个具有一些属性(键-值对)的节点来存储数据

  • 语法

  • 示例

3.创建多个标签的节点

  • 语法

  • 示例

2.5.2 CREATE…MATCH…RETURN
  • Neo4j CQL MATCH 命令用于:

    • 从数据库获取有关节点和属性的数据
    • 从数据库获取有关节点、关系和属性的数据
    • 不能单独使用 MATCH 命令从数据库检索数据。 如果单独使用它,将 InvalidSyntax 错误
  • Neo4j CQL RETURN 用于:

    • 检索节点的某些属性
    • 检索节点的所有属性
    • 检索节点和关联关系的某些属性
    • 检索节点和关联关系的所有属性
  • 语法

  • 示例 1:

2.5.4 WHERE
  • Neo4j CQL WHERE 过滤 MATCH 查询的结果

  • 语法

    • <comparison-operator>:

      • =
      • <>
      • <
      • >
      • <=
      • >=
    • <boolean-operator>

      • AND
      • OR
      • NOT
      • XOR
  • 使用 WHERE 子句创建关系

    • 语法

    • 示例

  • 示例

2.5.5 DELETE
  • Neo4j 使用 CQL DELETE 用来:

    • 删除节点
    • 删除节点及相关节点和关系

1.删除节点

  • 语法

  • 示例

2.删除节点和关系

  • 语法

  • 示例

2.5.6 REMOVE
  • Neo4j CQL REMOVE 删除节点或关系的现有属性

  • 语法

  • 示例1

  • 示例2

2.5.7 SET
  • Neo4j CQL 已提供 SET 子句来执行以下操作:

    • 向现有节点或关系添加新属性
    • 添加或更新属性值
  • 语法

  • 示例

2.5.8 ORDER BY
  • Neo4j CQL ORDER BY 对 MATCH 查询返回的结果进行排序

  • 语法

    ORDER BY <property-name-list> [DESC]

    ORDDR BY

    <node-label-name>.<property1-name> [DESC], <node-label-name>.<property2-name> [DESC], …. <node-label-name>.<propertyn-name> [DESC]

  • 示例

2.5.9 Sorting
2.5.10 UNION
  • Neo4j CQL 与 SQL 一样,有两个语句可以将两个不同的结果合并成一组结果

    • UNION

      • 将两组结果中的公共行组合并返回到一组结果中, 会进行去重

      • 限制

        • 结果列的 名称 和 类型 和来自两组结果的名称、类型必须匹配
      • 语法

      • 示例

    • UNION ALL

      • 将两组结果中的公共行组合并返回到一组结果中, 不会进行去重

      • 限制

        • 结果列的 名称 和 类型 和来自两组结果的名称、类型必须匹配
      • 语法

        • UNION
      • 示例

        • UNION
2.5.11 LIMIT、SKIP
  • LIMIT

    • Neo4j CQL 提供 LIMIT 子句来过滤或限制查询返回的行数, 它修剪CQL查询结果集底部的结果

    • 语法

    • 示例

  • SKIP

    • Neo4j CQL 提供 SKIP 来过滤或限制查询返回的行数, 它修整了CQL查询结果集顶部的结果

    • 语法

    • 示例

2.5.12 MERGE
  • Neo4j CQL MERGE 命令:

    • 创建节点,关系和属性
    • 为从数据库检索数据
    • MERGE 命令是 CREATE 命令和 MATCH 命令的组合
    • Neo4j CQL MERGE 命令在图中搜索给定模式,如果存在则返回结果; 如果不存在于图中,则创建新的节点/关系并返回结果
  • 语法

2.5.13 NULL
  • Neo4j CQL 将空值视为对节点或关系的属性的缺失值或未定义值

    • 当创建一个具有现有节点标签名称但未指定其属性值的节点时,它将创建一个具有 NULL 属性值的新节点
  • 示例

2.5.14 IN
  • Neo4j CQL 提供了一个 IN 运算符,以便为 CQL 命令提供值的集合

  • 语法

  • 示例

2.5.15 图形字体
  • 使用 Neo4j 数据浏览器来执行和查看 Neo4j CQL 命令或查询的结果, Neo4j 数据浏览器包含两种视图来显示查询结果:

    • UI查看
    • 网格视图
  • 默认情况下,Neo4j数据浏览器以小字体显示节点或关系图,并在UI视图中显示默认颜色

2.5.16 ID 属性
  • 在 Neo4j 中,Id 是节点和关系的默认内部属性. 这意味着,当我们创建一个新的节点或关系时,Neo4j 数据库服务器将为内部使用分配一个数字。它会自动递增。

  • 以相同的方式,Neo4j 数据库服务器为关系分配一个默认 Id 属性

    • 节点的 Id 属性的最大值约为 35 亿
    • 关系的 Id 属性的最大值约为 35 亿
2.5.17 Caption
  • 在 Neo4j 数据中,当我们在 Neo4j DATA 浏览器中执行 MATCH + RETURN 命令以查看 UI 视图中的数据时, 通过使用它们的 Id 属性显示节点和/或关系结果。它被称为 CAPTIONid 属性

2.6 CQL 函数

2.6.1 Neo4j CQL 字符串函数
  • Neo4J CQL 提供了一组 String 函数,用于在 CQL 查询中获取所需的结果

    • UPPER
    • LOWER
    • SUBSTRING
    • REPLACE
2.6.2 Neo4j CQL AGGREGATION 聚合函数
  • Neo4j CQL 提供了一些在 RETURN 子句中使用的聚合函数

    • COUNT
    • MAX
    • MIN
    • SUM
    • AVG
2.6.3 Neo4j CQL 关系函数
  • Neo4j CQL 提供了一组关系函数,以在获取开始节点,结束节点等细节时知道关系的细节

    • STARTNODE
    • ENDNODE
    • ID
    • TYPE

2.7 Admin 管理员

2.7.1 Neo4j 数据库备份和恢复
2.7.2 Neo4j CQL 索引
2.7.3 Neo4j CQL UNIQUE 约束
2.7.4 Neo4j CQL DROP UNIQUE

2.8 Neo4j 实战

本文通过一个实际的案例来一步一步教你使用 Cypher 来操作 Neo4j。

  • 案例的节点主要包括人物和城市两类,人物和人物之间有朋友、夫妻等关系,人物和城市之间有出生地的关系。
  1. 首先,我们删除数据库中以往的图,确保一个空白的环境进行操作

  2. 创建人物节点

Note

  • node-name: n
  • node-label-name: Person
  • <id>: 0
  • name: “John”
  1. 创建地区节点

  2. 创建人物之间的关系

  3. 创建人物-地区之间的关系

  4. 查询-所有在 Boston 出生的人物

  5. 查询所有对外有关系的节点

  6. 查询所有有关系的节点

  7. 查询所有对外有关系的节点,以及关系类型

  8. 查询所有有结婚关系的节点

  9. 创建节点的时候就建好关系

  10. 查询某人的朋友的朋友

  11. 增加、修改节点的属性

  12. 删除节点的属性

  13. 删除节点

  14. 删除有关系的节点

  15. 查询所有节点、关系

3.py2neo

  • 安装

    $ pip install --upgrade py2neo
    
  • 使用

    from py2neo import Graph
    
  • 核心 API

    • Graph class

      • Subgraph class

        • Node object
        • Relationship object

2.1 py2neo.database

from py2neo import Graph

graph = Graph(password = "password")
graph.run("UNWIND range(1, 3) AS n RETURN n, n * n as n_sq").to_table()

2.1.1 连接

  • GraphService objects

  • Graph

    • auto
    • begin
    • call
    • create
    • delete
    • delete_all()
    • evaluate
    • exists
    • match
    • match_one
    • merge
    • name
    • nodes
    • play
    • pull
    • push(subgraph)
    • read(cypher, parameters = None, **kwargs)
    • relationships
    • run(cypher, parameters = None, **kwargs)
    • schema
    • separate
    • service
  • SystemGraph objects

  • Schema objects

  • GraphService objects

  • ProcedureLibrary objects

  • Procedure objects

    • class py2neo.database.Procedure(graph, name)
  • py2neo

NLP Pipeline

1.构建 NLP Pipeline

_images/London.gif

1.0 数据

Important

London is the capital and most populous city of England and the United Kingdom. Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia. It was founded by the Romans, who named it Londinium.

(Source: Wikipedia article “London” )

上面这段文本包含了一些有用的事实,我们需要让计算机能够理解以下内容:

  • London is a city
  • London is located in England
  • London was settled by Romans
  • and so on

1.1 Step 1: 分句(Sentence Segmentation)

NLP Pipeline 的第一步就是 将文本分割为句子:

  • 1.”London is the capital and most populous city of England and the United Kingdom.”
  • 2.”Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia.”
  • 3.”It was founded by the Romans, who named it Londinium.”

1.2 Step 2: 分词(Word Tokenization)

  • 句子:

    _images/London_sentences.png
  • 分词(tokenization):

    _images/London_word.png

Note

Tokenization 对于英文来说非常容易,可以根据单词之间的空格对文本句子进行分割。 并且,也可以将标点符号也看成一种分割符,因为标点符号也是有意义的。

1.3 词性标注(Predicting Parts of Speech for Each Token)

通过词性标注,可以知道每个词在句子中的角色,即词性,词性包括名词(noun)、 动词(verb)、形容词(adjective)等等,这样可以帮助计算机理解句子的意思.

通过将每个单词(以及其周围的一些额外单词用于上下文)输入到预先训练的词性分 类模型中进行词性标注. 词性分类模型完全基于统计数据根据以前见过的相似句子 和单词来猜词性。

  • 词性标注过程

    _images/sentence1.png
  • 词性标注结果

    _images/sentence2.png

通过词性标注后的信息,可以收集到一些关于句子的基本含义了,例如:可以看到句子中看到有”London”、 和 “captial” 两个名词(Noun),因此该句子可能在谈论 “London”.

1.4 (Text Lemmatization)

在 NLP 中,称此过程为 词性限制(lemmatization),即找出句子中每个单词的最基 本形式或词缀(lemma)。

  • 示例:

    在自然语言中,单词或词会以不同的形式同时出现:

    • I had a pony.
    • I had two ponies.

    上面两个句子中都在表达 pony,但是两个句子中使用了 pony 不同的词缀。

    在计算机处理文本时,了解每个单词的基本形式很有帮助,这样就可以知道两个句子都在 表达相同的概念,否则,字符串 “pony” 和 “ponies” 对于计算机来说就像两个完全 不同的词.

    同样对于动词(verb)道理也一样:

    • I had two ponies.
    • I have two pony.
  • London 句子示例:

    _images/sentence3.png

1.5 识别停用词(Identifying Stop Words)

停用词的识别主要是用来考虑句子中每个单词的重要性。

自然语言汇总有很多填充词,例如: “and”,”the”,”a”,这些词经常出现。 在对文本进行统计时,这些填充词会给文本数据带来很多噪音,因为他们比其他 单词更频繁地出现。一些 NLP Pipeline 会将它们标记为 停用词(Stop Word)。 这些停用词需要在进行任何统计分析之前将其过滤掉。

  • London 示例:

    _images/sentence4.png

Note

  • 通常仅通过检查已知停用词的硬编码列表即可识别停用词.
  • 没有适用于所有应用程序的标准停用词列表,要忽略的单词列表可能会因应用程序而异.
  • 例如:如果要构建摇滚乐队搜索引擎,则要确保不要过滤到单词 “The”。因为,”The” 一词 会经常出现在乐队的名字中,比如 1980年代的注明摇滚乐队 “The The!”.

1.6 句法依赖分析(Dependency Parsing)

1.7 命名实体识别

1.8 共指解析

2.构建 Python NLP Pipeline

_images/NLP_Pipeline.png

3.工具

NLP–Solr 搜索引擎

1.Solr 简介

  • 基于关键字查询的搜索引擎
    • 缺点:难以构造出准确表达用户需求的查询请求,返回的结果冗余甚至无用的信息多
  • 基于 NLP 处理的搜索引擎
    • 优点:能够更好地理解用户的查询意图,更准确地推荐相关查询请求,并且高效地返回更相关的查询结果
  • Lucene
    • Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而为应用程序提供索引和搜索功能
    • Lucene 是 Apache Jakarta(雅加达)家族中的一个开源项目,也是目前最为流行的基于 Java 开源全文检索工具包
    • 目前已经有很多应用程序的搜索功能是基于 Lucene,比如 Eclipse 帮助系统的搜索功能
    • Lucene 能够为文本类型的数据建立索引,所以只要把要索引的数据格式转化为文本格式,Lucene 就能对文档进行索引和搜索
  • Solr
    • Solr 与 Lucene 并不是竞争关系,Solr 依存于 Lucene,因为 Solr 底层的核心技术是使用 Lucene 来实现的

    • Solr 和 Lucene 的本质区别有三点:

      • 搜索服务器
        • Lucene 本质上是搜索库,不是独立的应用程序,而 Solr 是
      • 企业级
        • Lucene 专注于搜索底层的建设,而 Solr 专注于企业应用
      • 管理
        • Lucene 不负责支撑搜索服务所必需的管理,而 Solr 负责
    • Solr 是 Lucene 面向企业搜索应用的扩展

  • Solr 与 NLP 的关系
    • 在 NLP 的处理过程中,有一些场景,比如人机交互,是需要实时或近似实时的。 在人机对话中,用户所关心的一些常用问题、答案会尽可能预存在 Solr 中做检索, 当用户提问机器人的时候,NLP 算法会先理解问题的语义,之后将“翻译”后的语言推送给 Solr, 由 Solr 负责检索预存的问题,将最匹配用户提问的那个答案返回给用户;

2.全文检索的原理

  • 全文检索

    • 比如,电脑里有一个文件夹,文件夹中存储了很多文件,例如 Word、Excel、PPT,希望根据搜索关键字的方式搜索到相应的文档, 比如输入 Solr,所有内容含有 Solr 这个关键字的文件就会被筛选出来,这个就是全文检索
  • 搜索方法(非结构化数据)

    • 顺序扫描法(Serial Scanning)

      • 比如要找内容包含某一个字符串的文件,就是一个文档一个文档地看,对于每一个文档,从头看到尾, 如果该文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,知道扫描完所有的文件
      • Linux 下的 grep 命令就是利用了顺序扫描法来寻找包含某个字符串的文件,这种方法针对小数据量的文件来说最直接, 但是对于大量文件,查询效率就很低了
    • 全文检索

      • 对非结构化数据顺序扫描很慢,对结构化数据的搜索却相对较快,把非结构化数据转化为结构化数据构成了全文检索的基本思路, 也就是将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对这些有一定结构的数据进行搜索, 从而达到搜索相对较快的目的

        • 从非结构化数据中提取出然后重新组织的信息,称为 索引
        • 先建立索引,再对索引进行搜索的过程,就叫做 全文检索(full-text search)
    • 顺序扫描 vs 全文检索

      • 创建索引之后进行检索与顺序扫描的区别在于,顺序扫描时每次都要扫描,而创建索引的过程仅仅需要一次,以后便是一劳永逸了, 每次搜索,创建索引的过程不必经过,仅仅搜索创建好的索引就可以了。这也是全文搜索相对于顺序扫描的优势之一:

        • 一次索引,多次使用

3.Solr 简介和部署

3.1 Solr 简介

  • Solr 是一种开发源码的、基于 Lucene Java 的搜索服务器,易于加入 Web 应用程序中

  • Solr 提供了层面搜索(就是统计)、命中醒目显示并且支持多种输出格式

    • XML
    • XSLT
    • JSON
  • Solr 易于安装和配置,而且附带了一个基于 HTTP 的管理页面

  • 可以使用 Solr 的基本搜索功能,也可以对它进行扩展从而满足企业的需要

  • Solr 的特性:

    • (1)高级的全文搜索功能
    • (2)转为高质量的网络流量进行的优化
    • (3)基于开放接口(XML、HTTP)的标准
    • (4)综合的 HTML 管理页面
    • (5)可伸缩性–能够有效地复制到另一个 Solr 搜索服务器
    • (6)使用 XML 配置达到灵活性和适配性
    • (7)可扩展的插件体系

3.2 Solr 部署

  • 1.下载 Tomcat、Solr、JDK 安装包

  • 2.规划安装目录

    • Solr 安装在 Linux 的 /opt/bigdata/ 目录中

      $ mkdir -p /opt/bigdata
      
  • 3.将下载好的 Tomcat、Solr、JDK 包移动到 /opt/bigdata/ 下,解压、安装

    • (1)解压 Tomcat、Solr

      $ cd /opt/bigdata
      
      $ tar -zxvf apache-tomcat-8.5.24.tar.gz
      $ tar -zxvf solr-6.5.1.gz
      
      $ mv /opt/bigdata/apache-tomcat-8.5.24 /opt/bigdata/tomcat
      $ mv /opt/bigdata/solr-6.5.1 /opt/bigdata/solr
      
    • (2)JDK 安装

      $ rpm -ivh jdk-8u144-linux-x64.rpm
      $ java -version
      
    • (3)目录结构

  • 4.Solr 集成 Tomcat

    • Solr 需要运行在一个 Servlet 容器中,Solr 6.5.1 要求 JDK 使用 1.8 以上版本,Solr 默认提供 Jetty(Java 写的 Servlet 容器), 这里使用 Tomcat 作为 Servlet 容器

    • (1)创建 Tomcat 的 Solr 工程

      # 复制 Solr 中的 webapp 到 Tomcat webapps
      $ cp /opt/bigdata/solr/server/solr-webapp/webapp /opt/bigdata/tomcat/webapps/
      $ mv /opt/bigdata/tomcat/webapps/webapp /opt/bigdata/tomcat/webapps/solr
      
      # 复制 Solr 的 jar 包到 Tomcat 的 Solr 工程中
      $ cp /opt/bigdata/solr/server/lib/ext/* /opt/bigdata/tomcat/webapps/solr/WEB-INF/lib
      $ cp /opt/bigdata/solr/server/lib/metric*.jar /opt/bigdata/tomcat/webapps/solr/WEB-INF/lib
      $ cp /opt/bigdata/solr/dist/*.jar /opt/bigdata/tomcat/webapps/solr/WEB-INF/lib
      
    • (2)创建 solrhome 文件夹,solrhome 是存放 Solr 服务器所有配置文件的目录

      $ mkdir /opt/bigdata/solrhome
      $ cp -r /opt/bigdata/solr/server/solr/* /opt/bigdata/solrhome
      
    • (3)添加 log4j 的配置文件

      # 创建 classes 目录
      $ mkdir /opt/bigdata/tomcat/webapps/solr/WEB-INF/classes
      # 复制 log4j.properties 文件
      $ cp /opt/bigdata/solr/server/resources/log4j.properties /opt/bigdata/tomcat/webapps/solr/WEB-INF/classes
      
    • (4)在 Tomcat 的 Solr 工程的 web.xml 文件中指定 solrhome 的位置

      $ cd /opt/bigdata/tomcat/webapps/solr/WEB-INF/
      $ vim web.xml
      
      <env-entry>
         <env-entry-name>solr/home</env-entry-name>
         <env-entry-value>/opt/bigdata/solrhome</env-entry-value>
         <env-entry-type>java.lang.String</env-entry-type>
      </env-entry>
      
    • (5)启动 Tomcat

      $ /opt/bigdata/tomcat/bin/startup.sh
      
    • (6)建立 solrcore

      $ cd /opt/bigdata/solrhome
      $ mkdir my_core
      $ cp /opt/bigdata/solr/server/solr/configsets/sample_techproducts_config/conf /opt/bigdata/solrhome/my_core
      
    • (7)访问 Solr 控制台界面主页

4.Solr 后台管理描述

5.Solr 管理索引库

NLP–特征提取

Text feature extraction
  • API

    • feature_extraction.text.CountVectorizer
    • feature_extraction.text.HasingVectorizer
    • feature_extraction.text.TfidfTransformer
    • feature_extraction.text.TfidfVectorizer

1.Bag of Words

  • scikit-learn 提供的工具

    • tokenizing
    • counting
    • normalizing

2.Sparsity

test

3.Common Vectorizer usage

4.TF-IDF term weighting

5.Decoding text files

NLP–seq2seq

1.encoder-decoder、seq2seq 简介

自然语言处理的很多应用中, 输入和输出都可以是不定长序列. 当输入和输出都是不定长序列时, 我们可以使用 编码器—解码器(encoder-decoder) 或者 seq2seq 模型。 这两个模型本质上都用到了两个循环神经网络, 分别叫做 编码器解码器

  • 编码器用来分析输入序列
  • 解码器用来生成输出序列

以机器翻译为例, 输入可以是一段不定长的英语文本序列, 输出可以是一段不定长的法语文本序列, 例如

  • 英语输入:“They”、“are”、“watching”、“.”
  • 法语输出:“Ils”、“regardent”、“.”
_images/seq2seq.png

上图描述了使用编码器—解码器将上述英语句子翻译成法语句子的一种方法:

  • 序列表示特殊符号

    • 在训练数据集中, 可以在每个句子后附上特殊符号 <eos> (end of sequence)以表示序列的终止
    • 解码器在最初时间步的输入用到了一个表示序列开始的特殊符号 <bos> (beginning of sequence)
  • 编码器

    • 编码器每个时间步的输入依次为:英语句子中的单词、标点、特殊符号“<eos>”
    • 使用编码器在最终时间步的隐藏状态作为输入句子的表征或编码信息
  • 解码器

    • 解码器在各个时间步中使用输入句子的编码信息和上个时间步的输出以及隐藏状态作为输入
    • 希望解码器在各个时间步能正确依次输出翻译后的:法语单词、标点和特殊符号“<eos>”

2.编码器

编码器的作用是把一个不定长的输入序列变换为一个定长的背景变量 \(c\), 并在该背景变量中编码输入序列信息。 编码器可以使用神经网络。

让我们考虑批量大小为 1 的时序数据样本。假设输入序列是 \(x_1, \cdots, x_T\), 例如 \(x_i\) 是输入句子中的第 \(i\) 个词。

在时间步 \(t\), 循环神经网络将输入 \(x_t\) 的特征向量 \(\boldsymbol{x}_t\) 和上个时间步的隐藏状态 \(\boldsymbol{h}_{t-1}\) 变换为当前时间步的隐藏状态 \(\boldsymbol{h}_t\)。我们可以用函数 \(f\) 表达循环神经网络隐藏层的变换:

  • \(\boldsymbol{h}_t = f(\boldsymbol{x}_t, \boldsymbol{h}_{t-1}).\)

接下来, 编码器通过自定义函数 \(q\) 将各个时间步的隐藏状态变换为背景变量:

  • \(\boldsymbol{c} = q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T)\)

例如, 当选择 \(q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T) = \boldsymbol{h}_T\) 时, 背景变量是输入序列最终时间步的隐藏状态 \(h_T\).

以上描述的编码器是一个单向的循环神经网络, 每个时间步的隐藏状态只取决于该时间步及之前的输入子序列。 我们也可以使用双向循环神经网络构造编码器。在这种情况下, 编码器每个时间步的隐藏状态同时取决于该时间 步之前和之后的子序列(包括当前时间步的输入), 并编码了整个序列的信息。

3.解码器

编码器输出的背景变量 \(c\) 编码了整个输入序列 \(x_1, \cdots, x_T\) 的信息。

给定训练样本中的输出序列 \(y_1, y_2, \ldots, y_{T'}\), 对每个时间步 \(t'\), 解码器输出 \(y_t'\) 的条件概率将基于之前的输出序列 \(y_1, y_2, \ldots, y_{t'-1}\) 和背景变量 \(c\), 即 \(P(y_{t^\prime} \mid y_1, \ldots, y_{t^\prime-1}, \boldsymbol{c})\)

为此, 我们可以使用另一个循环神经网络作为解码器。在输出序列的时间步 \(t'\), 解码器将上一时间步的输出 \(y_t′−1\) 以及背景变量 \(c\) 作为输入, 并将它们与上一时间步的隐藏状态 \(s_t′−1\) 变换为当前时间步的隐藏状态 \(s_t′\)。 因此, 我们可以用函数 \(g\) 表达解码器隐藏层的变换:

  • \(\boldsymbol{s}_{t^\prime} = g(y_{t^\prime-1}, \boldsymbol{c}, \boldsymbol{s}_{t^\prime-1}).\)

有了解码器的隐藏状态后, 我们可以使用自定义的输出层和 softmax 运算来计算 \(P(y_{t^\prime} \mid y_1, \ldots, y_{t^\prime-1}, \boldsymbol{c})\), 例如, 基于当前时间步的解码器隐藏状态 \(\boldsymbol{s}_{t^\prime}\)、上一时间步的输出 \(y_{t^\prime-1}\) 以及 背景变量 \(c\) 来计算当前时间步输出 \(y_{t^\prime}\) 的概率分布。

4.训练模型

根据最大似然估计, 我们可以最大化输出序列基于输入序列的条件概率

\(\begin{split}\begin{aligned} P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T) &= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, x_1, \ldots, x_T)\\ &= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}), \end{aligned}\end{split}\)

并得到该输出序列的损失

\(- \log P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T) = -\sum_{t'=1}^{T'} \log P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}).\)

在模型训练中, 所有输出序列损失的均值通常作为需要最小化的损失函数。在上面图所描述的模型预测中, 我们需要将解码器在上一个时间步的输出作为当前时间步的输入。与此不同, 在训练中我们也可以将标签 序列(训练集的真实输出序列)在上一个时间步的标签作为解码器在当前时间步的输入。这叫作强制教学(teacher forcing)。

5.总结

  • 编码器-解码器(seq2seq)可以输入并输出不定长的序列
  • 编码器—解码器使用了两个循环神经网络
  • 在编码器—解码器的训练中, 可以采用强制教学

6.参考文献

  • [1] Cho, K., Van Merriënboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., & Bengio, Y. (2014). Learning phrase representations using RNN encoder-decoder for statistical machine translation. arXiv preprint arXiv:1406.1078.
  • [2] Sutskever, I., Vinyals, O., & Le, Q. V. (2014). Sequence to sequence learning with neural networks. In Advances in neural information processing systems (pp. 3104-3112).

ResNet-V1

Deep Residual Learning for Image Recognition

Kaiming He, Xianyu Zhang, Shaoqing Ren, Jian Sun

Keras-CIFAR10-ResNet.md

LeNet-5

Gradient-based learning applied to document recognition

LeNet-5 结构分析

\(输入-卷积-池化-卷积-池化-卷积(全连接)-全连接-全连接(输出)\)

各层网路结构和参数:

  • 输入层
    • 32 x 32 x 1
  • C1 层
    • 卷积层
    • 输入: 32 x 32 x 1
      • 滤波器大小: 5 x 5 x 1
      • 滤波器个数: 6
    • 输出: 28 x 28 x 6【TODO】
    • 参数个数: 5 x 5 x 1 x 6 = 156
  • P2 层
    • 池化层
    • 输入: 28 x 28 x 6
      • 滤波器大小: 2 x 2
      • 滤波器个数: 6
    • 输出: 14 x 14 x 6
    • 参数个数: 2 x 6 = 12【TODO】
    • 平均池化
  • C3 层
    • 卷积层
    • 输入: 14 x 14 x 6
      • 滤波器大小: 5 x 5 x 6
      • 滤波器个数: 16
    • 输出: 10 x 10 x 16
    • 参数个数: 5 x 5 x 6 x 16 = 2416
  • P4 层
    • 池化层
    • 输入: 10 x 10 x 16
      • 滤波器: 2 x 2
      • 滤波器个数: 16
    • 输出: 5 x 5 x 16
    • 参数个数: 2 x 16 = 32
    • 平均池化
  • C5 层
    • 论文中是个卷积层,但滤波器大小为 5 x 5,所以其本质上是个全连接层。如果将 5 x 5 x 16 拉成一个向量,它就是一个全连接层。
    • 输入: 5 x 5 x 16
      • 滤波器大小: 5 x 5 x 16
      • 滤波器个数: 120
    • 输出: 1 x 1 x 120
    • 参数个数: 5 x 5 x 16 x 120 + 120 = 48120
  • F6 层
    • 全连接层
      • 激活函数: \(tanh\)
    • 输入: 120
    • 输出: 84
    • 参数个数: 120 x 84 + 84
  • F7 层
    • 全连接层,输出层
    • 输入: 84
    • 输出: 10
    • 参数个数: 84 x 10 + 10

LeNet-5 TensorFlow 实现

步骤:

  1. 定义创建输入、输出的占位符变量模块
  2. 初始化各层参数模块
  3. 创建前向传播模块
  4. 定义模型优化迭代模块
  5. 设置输入数据
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
def create_placeholder():
     X = tf.placeholder(tf.float32, shape = (None, 28, 28))
     Y = tf.placeholder(tf.float32, shape = (None, 10))
     keep_prob = tf.placeholder(tf.float32)
     return X, Y, keep_prob
def main():
     mnist = input_data.read_data_sets("./data/", one_hot = True)
     batch_size = 128
     learning_rate = 0.001
     display_step = 10
     test_step = 5000
     num_steps = 10000
     dropout = 0.5
     l2_lambda = 0.0001
     lenet_model()


if __init__ == "__main__":
     main()

GPU 硬件

1.概述

_images/GPU.png

深度学习训练通常需要大量的计算资源。GPU 目前是深度学习最常使用的计算加速硬件。 相对于 CPU 来说,GPU 更便宜且计算更加密集。

  • 一方面,相同计算能力的 GPU 的价格一般是 CPU 价格的十分之一;
  • 另一方面,一台服务器通常可以搭载 8 块或者 16 块 GPU。因此,GPU 数量可以看作是衡量一台服务器的深度学习计算能力的一个指标。

2.GPU 选择

目前独立显卡主要有 AMD 和 NVIDIA 两家厂商。其中 NVIDIA 在深度学习布局较早,对深度学习框架支持更好。 因此,目前大家主要会选择 NVIDIA 的 GPU。

NVIDIA 有面向个人用户(如 GTX 系列)和企业用户(如 Tesla 系列)的两类 GPU。这两类 GPU 的计算能力相当。 然而,面向企业用户的 GPU 通常使用被动散热并增加了显存校验,从而更适合数据中心,并通常要比面向个人用户的 GPU 贵上 10 倍。

如果是拥有 100 台机器以上的大公司用户,通常可以考虑针对企业用户的 NVIDIA Tesla 系列。 如果是拥有 10~100 台机器的实验室和中小公司用户,预算充足的情况下可以考虑 NVIDIA DGX 系列, 否则可以考虑购买如 Supermicro 之类的性价比比较高的服务器,然后再购买安装 GTX 系列的 GPU。

NVIDIA 一般每一两年发布一次新版本的 GPU,例如 2016 年发布的 GTX 1000 系列以及 2018 年发布的 RTX 2000系列。 每个系列中会有数个不同的型号,分别对应不同的性能。

GPU 的性能主要由以下 3 个参数构成:

  • 计算能力。通常我们关心的是 32 位浮点计算能力。16 位浮点训练也开始流行,如果只做预测的话也可以用 8 位整数。
  • 显存大小。当模型越大或者训练时的批量越大时,所需要的显存就越多。
  • 显存带宽。只有当显存带宽足够时才能充分发挥计算能力。

对大部分用户来说,只要考虑计算能力就可以了。显存尽量不小于 4GB。但如果 GPU 要同时显示图形界面,那么推荐的显存大小至少为6GB。 显存带宽通常相对固定,选择空间较小。

下图描绘了 GTX 900 和 GTX 1000 系列里各个型号的 32 位浮点计算能力和价格的对比(其中的价格为Wikipedia的建议价格)。

_images/gtx.png

我们可以从上图中读出以下两点信息:

  1. 在同一个系列里面,价格和性能大体上成正比。但后发布的型号性价比更高,如 980 Ti和 1080 Ti。
  2. GTX 1000系列比900系列在性价比上高出2倍左右。

如果大家继续比较 NVIDIA 的一些其他系列,也可以发现类似的规律。据此,我们推荐大家在能力范围内尽可能买较新的 GPU。

_images/GPU_compare1.png _images/GPU_compare2.png

3.整机配置

通常,我们主要用 GPU 做深度学习训练。因此,不需要购买高端的 CPU。至于整机配置,尽量参考网上推荐的中高档的配置就好。 不过,考虑到 GPU 的功耗、散热和体积,在整机配置上也需要考虑以下 3 个额外因素:

  1. 机箱体积。显卡尺寸较大,通常考虑较大且自带风扇的机箱。
  2. 电源。购买 GPU 时需要查一下 GPU 的功耗,如 50W 到 300W 不等。购买电源要确保功率足够,且不会造成机房供电过载。
  3. 主板的 PCIe 卡槽。推荐使用 PCIe 3.0 16x 来保证充足的 GPU 到内存的带宽。如果搭载多块 GPU,要仔细阅读主板说明, 以确保多块 GPU 一起使用时仍然是 16 倍带宽。注意,有些主板搭载 4 块 GPU 时会降到 8 倍甚至 4 倍带宽。

4.总结

  • 在预算范围内,尽可能买较新的 GPU
  • 整机配置需要考虑到 GPU 的功耗、散热、体积等
_images/work_hub.png

GPU 软件

Important

系统:Ubuntu 20.04

1.安装 Ubuntu 20.04

  • test

2.安装 Nvidia 显卡驱动

最简单的方式是通过系统的软件与更新来安装:

  1. 进入系统的图形桌面,打开 Software & Updates 软件,可以看到标签栏有一个 Additional Drivers:

    • NVIDIA Corporation: Unknown

      • Using NVIDIA dirver metapackage from nvidia-driver-455(proprietary, tested)
      • Using X.Org x server – Nouveau display driver from xserver-xorg-video-nouveau(open source)
    • 选择第一个安装 Nvidia 官方驱动(第二个是开源驱动)即可,根据网络情况稍等大概十分钟,安装完重启服务器。

  2. 重启完之后更新一下软件

    sudo apt update
    sudo apt upgrade
    

    这里会连带 Nvidia 的驱动一起神级一遍,更新到最新的驱动;更新完可能会出现 nvidia-smi 命令报错,再重启一遍就解决了。

3.安装 CUDA

3.1 CUDA 介绍

NVIDIA® CUDA® 工具包提供了开发环境,可供创建经 GPU 加速的高性能应用。借助 CUDA 工具包,可以在经 GPU 加速的嵌入式系统、台式工作站、企业数据中心、基于云的平台和 HPC 超级计算机中开发、优化和部署应用。 此工具包中包含多个 GPU 加速库、多种调试和优化工具、一个 C/C++ 编译器以及一个用于在主要架构(包括 x86、Arm 和 POWER) 上构建和部署应用的运行时库。

借助多 GPU 配置中用于分布式计算的多项内置功能,科学家和研究人员能够开发出可从单个 GPU 工作站扩展到配置数千个 GPU 的云端设施的应用。

3.2 CUDA 安装

  1. 如果之前安装了旧版本的 CUDA 和 cudnn 的话,需要先卸载后再安装, 卸载 CUDA:

    sudo apt-get remove --purge nvidia
    

    然后重新安装显卡驱动,安装好了之后开始安装 CUDA。

  2. 下载 CUDA 安装包–CUDA Toolkit 11.0 Download|NVIDIA Developer

    _images/cuda.png
  3. 安装 CUDA

    chmod +x cuda_11.0.2_450.51.05_linux.run
    sudo sh ./cuda_11.0.2_450.51.05_linux.run
    
  4. 配置 UDA 环境变量

    vim ~/.bashrc
    # or
    vim ~/.zsh
    
    export CUDA_HOME=/usr/local/cuda-11.0
    export LD_LIBRARY_PATH=${CUDA_HOME}/lib64
    export PATH=${CUDA_HOME}/bin/${PATH}
    
    source ~/.bashrc
    
  5. 查看安装的版本信息

    nvcc -V
    

    可以编译一个程序测试安装是否成功,执行以下几条命令:

    cd ~/Softwares/cuda/NVIDIA_CUDA-11.0_Samples/1_Utilities/deviceQuery
    make
    ./deviceQuery
    

参考链接:

4.安装 cuDNN

  1. 下载 cuDNN 安装包–cuDNN Download|NVIDIA Developer

  2. 安装 cuDNN

    tar -xzvf cudnn-11.0-linux-x64-v8.0.5.39.tag
    sudo cp cuda/lib64/* /usr/local/cuda-11.0/lib64/
    sudo cp cuda/include/* /usr/local/cuda-11.0/include/
    
  3. 查看 cuDNN 的版本信息

    cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2

5.安装 Conda 环境

不同的训练框架和版本可能会需要不同的python版本相对应,而且有的包比如numpy也对版本有要求, 所以比较优雅的方法是给每个配置建立一个虚拟的python环境,在需要的时候可以随时切换, 而不需要的时候也能删除不浪费磁盘资源,那在这方面conda是做得最好的。

  1. 下载 Anaconda/MiniConda 安装包–Anaconda|Individdual Edition

  2. 安装 Conda

    chmod +x Anaconda3-2020.11-Linux-x86_64.sh
    ./Anaconda3-2020.11-Linux-x86_64.sh
    

6.安装 Nvidia-Docker

7.测试

  1. 本地 Conda 环境

    conda create --name python_38-pytorch_1.7.0 python=3.8
    
    conda activate python_38-pytorch_1.7.0
    
    which pip
    
  2. 安装 PyTorch

    pip install torch==1.7.0+cu110 torchvision==0.8.1+cu110 torchaudio===0.7.0 -f https://download.pytorch.org/whl/torch_stable.html
    

7.1 TensorFlow

TensorFlow GPU 支持

Note

注意:对于 Ubuntu 和 Windows,需要安装支持 CUDA® 的显卡,才能实现 GPU 支持。

TensorFlow GPU 支持需要各种驱动程序和库。为了简化安装并避免库冲突, 建议使用支持 GPU 的 TensorFlow Docker 镜像(仅限 Linux)。 此设置需要 NVIDIA® GPU 驱动程序。

  1. pip 软件包

  2. 硬件要求

    支持以下带有 GPU 的设备:

    • CUDA® 架构为 3.5、3.7、5.2、6.0、6.1、7.0 或更高的 NVIDIA® GPU 卡。请参阅支持 CUDA® 的 GPU 卡列表。
    • 在配备 NVIDIA® Ampere GPU(CUDA 架构 8.0)或更高版本的系统上,内核已从 PTX 经过了 JIT 编译,因此 TensorFlow 的启动时间可能需要 30 多分钟。通过使用“export CUDA_CACHE_MAXSIZE=2147483648”增加默认 JIT 缓存大小,即可将此系统开销限制为仅在首次启动时发生(有关详细信息,请参阅 JIT 缓存)。
    • 对于 CUDA® 架构不受支持的 GPU,或为了避免从 PTX 进行 JIT 编译,亦或是为了使用不同版本的 NVIDIA® 库,请参阅在 Linux 下从源代码编译指南。
    • 软件包不包含 PTX 代码,但最新支持的 CUDA® 架构除外;因此,如果设置了 CUDA_FORCE_PTX_JIT=1,TensorFlow 将无法在旧版 GPU 上加载。(有关详细信息,请参阅应用兼容性。)
  3. 软件要求

    必须在系统中安装以下 NVIDIA® 软件:

    • NVIDIA® GPU 驱动程序:CUDA® 10.1 需要 418.x 或更高版本。
    • CUDA® 工具包:TensorFlow 支持 CUDA® 10.1(TensorFlow 2.1.0 及更高版本)
    • CUDA® 工具包附带的 CUPTI。
    • cuDNN SDK 7.6
    • (可选)TensorRT 6.0,可缩短用某些模型进行推断的延迟时间并提高吞吐量。
  4. Linux 设置

    • 要在 Ubuntu 上安装所需的 NVIDIA 软件,最简单的方法是使用下面的 apt 指令。 但是,如果从源代码构建 TensorFlow,请手动安装上述软件要求中列出的软件,并 考虑以 -devel TensorFlow Docker 映像作为基础。

    • 安装 CUDA® 工具包附带的 CUPTI,并将其安装目录附加到 $LD_LIBRARY_PATH 环境变量中:

      $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/extras/CUPTI/lib64
      
    • 使用 apt 安装 CUDA

      • Ubuntu 16.04、Ubuntu 18.04

      • CUDA® 10(TensorFlow 1.13.0 及更高版本),这些说明可能适用于其他 Debian 系发行版

      • Ubuntu 20.04(CUDA 10.1, 11.1)

      • Ubuntu 18.04(CUDA 10.1)

        # Add NVIDIA package repositories
        $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.1.243-1_amd64.deb
        $ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
        $ sudo dpkg -i cuda-repo-ubuntu1804_10.1.243-1_amd64.deb
        $ sudo apt-get update
        $ wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
        $ sudo apt install ./nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
        $ sudo apt-get update
        
        # Install NVIDIA driver
        $ sudo apt-get install --no-install-recommends nvidia-driver-450
        # Reboot. Check that GPUs are visible using the command: nvidia-smi
        
        # Install development and runtime libraries (~4GB)
        $ sudo apt-get install --no-install-recommends \
           cuda-10-1 \
           libcudnn7=7.6.5.32-1+cuda10.1  \
           libcudnn7-dev=7.6.5.32-1+cuda10.1
        
        # Install TensorRT. Requires that libcudnn7 is installed above.
        $ sudo apt-get install -y --no-install-recommends libnvinfer6=6.0.1-1+cuda10.1 \
           libnvinfer-dev=6.0.1-1+cuda10.1 \
           libnvinfer-plugin6=6.0.1-1+cuda10.1
        
      • Ubuntu 16.04(CUDA 10.1)

        # Add NVIDIA package repositories
        # Add HTTPS support for apt-key
        $ sudo apt-get install gnupg-curl
        $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_10.1.243-1_amd64.deb
        $ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
        $ sudo dpkg -i cuda-repo-ubuntu1604_10.1.243-1_amd64.deb
        $ sudo apt-get update
        $ wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1604/x86_64/nvidia-machine-learning-repo-ubuntu1604_1.0.0-1_amd64.deb
        $ sudo apt install ./nvidia-machine-learning-repo-ubuntu1604_1.0.0-1_amd64.deb
        $ sudo apt-get update
        
        # Install NVIDIA driver
        # Issue with driver install requires creating /usr/lib/nvidia
        $ sudo mkdir /usr/lib/nvidia
        $ sudo apt-get install --no-install-recommends nvidia-418
        # Reboot. Check that GPUs are visible using the command: nvidia-smi
        
        # Install development and runtime libraries (~4GB)
        $ sudo apt-get install --no-install-recommends \
           cuda-10-1 \
           libcudnn7=7.6.4.38-1+cuda10.1  \
           libcudnn7-dev=7.6.4.38-1+cuda10.1
        
        
        # Install TensorRT. Requires that libcudnn7 is installed above.
        $ sudo apt-get install -y --no-install-recommends \
           libnvinfer6=6.0.1-1+cuda10.1 \
           libnvinfer-dev=6.0.1-1+cuda10.1 \
           libnvinfer-plugin6=6.0.1-1+cuda10.1
        
  1. Windows 设置

    • 根据硬件、软件要求,参考 适用于Windows 的 CUDA 安装指南 进行安装

    • 确保安装的 NVIDIA 软件包版本一致,如果没有 cuDNN64_7.dll 文件,TensorFlow 将无法加载,如需使用其他版本, 需要使用源码构建: 在 Windows 下从源代码构建 .

    • 将 CUDA®、CUPTI 和 cuDNN 安装目录添加到 %PATH% 环境变量中。

      • 例如,如果 CUDA® 工具包安装到 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1, 并且 cuDNN 安装到 C:\toolscuda,请更新 %PATH% 以匹配路径:
      C:\> SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\bin;%PATH%
      C:\> SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\extras\CUPTI\lib64;%PATH%
      C:\> SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\include;%PATH%
      C:\> SET PATH=C:\tools\cuda\bin;%PATH%
      

7.2 PyTorch

PyTorch GPU 支持
import torch
torch.CUDA.is_available()

TPU 硬件

1.概述

2.test

TPU 软件

1.概述

2.test

TensorFlow Datasets

1.TensorFlow Datasets 库

  • 库安装:

    $ pip install tensorflow
    $ pip install tensorflow-datasets
    
  • 库导入:

    # tf.data, tf.data.Dataset
    import tensorflow as tf
    # tf.keras.datasets.<dataset_name>.load_data
    from tensorflow.keras import datasets
    # tfds.load
    import tensorflow_datasets as tfds
    

2.TensorFlow Dataset 介绍

2.1 TensorFlow Dataset 介绍

  • TensorFlow 提供了 tf.data 模块,它包括了一套灵活的数据集构建 API, 能够帮助快速、高效地构建数据输入的流水线,尤其适用于数据量巨大的情景

    • tf.data API 在 TensorFlow 中引入了两个新的抽象类:

      • tf.data.Dataset: 提供了对数据集的高层封装

        • tf.data.Dataset 由一系列可迭代访问的元素(element)组成,其中每个元素包含一个或多个 Tensor 对象

        • tf.data.Dataset 可以通过两种方式来创建数据集:

          • 创建来源:通过一个或多个 tf.Tensor 对象构建数据集
            • tf.data.Dataset.from_tensors()
            • tf.data.Dataset.from_tensor_slices()
          • 应用转换:通过一个或多个 tf.data.Dataset 对象构建数据集
            • tf.data.Dataset.map()
            • tf.data.Dataset.batch()
      • tf.data.Iterator

        • 提供了从数据集中提取元素的主要方法

        • tf.data.Iterator.get_next()

          • 返回的操作会在执行时生成Dataset的下一个元素,并且此操作通常当输入管道和模型之间的接口
        • tf.data.Iterator.initializer

          • 使用不同的数据集重新初始化和参数化迭代器
  • TensorFlow Datasets(tensorflow_datasets) 是可用于 TensorFlow 或其他 Python 机器学习框架(例如 Jax) 的一系列数据集。 所有数据集都作为 tf.data.Dataset 提供,实现易用且高性能的输入流水线。

  • TensorFlow 数据集 API

    • tf.data

      • tf.data.Dataset
      • tf.data.Dataset.from_tensor_slices
    • tensorflow_datasets

      • tensorflow_datasets.load(data, split, shuffle_files, as_supervised)
    • tf.keras.datasets

      • tf.keras.datasets.mnist.load_data()

2.2 TensorFlow Dataset 建立

1.建立 tf.data.Dataset 的最基本的方法是使用 tf.data.Dataset.from_tensor_slices()

  • 适用于数据量较小(能够将数据全部装进内存)的情况
  • 如果数据集中的所有元素通过张量的第 0 维拼接成一个大的张量
import tensorflow as tf
import numpy as np

X = tf.constant([2013, 2014, 2015, 2016, 2017])
Y = tf.constant([12000, 14000, 15000, 16500, 17500])

dataset = tf.data.Dataset.from_tensor_slices((X, Y))
for x, y in dataset:
   print(x.numpy(), y.numpy())

2.使用 tf.data.Dataset.from_tensor_slices()tf.keras.datasets.mnist.load_data()

import tensorflow as tf
import matplotlib.pyplot as plt

(train_data, train_label), (_, _) = tf.keras.datasets.mnist.load_data()
train_data = np.expand_dim(train_data.astype(np.float32) / 255, axis = -1)
mnist_dataset = tf.data.Dataset.from_tensor_slices((train_data, train_label))

for image, label in mnist_dataset.take(1):
   plt.title(label.numpy())
   plt.imshow(image.numpy())
   plt.show()

3.TensorFlow Datasets 提供了一系列可以和 Tensorflow 配合使用的数据集,它负责下载和准备数据,以及构建 tf.data.Dataset

  • 每一个数据集(dataset) 都实现了抽象基类 tfds.core.DatasetBuilder 来构建
import tensorflow_datasets as tfds

# 构建 tf.data.Dataset
dataset1 = tfds.load("mnist", split = "train", shuffle_files = True)
dataset2 = tfds.load("mnist", split = tfds.Split.TRAIN, as_supervised = True)

# 构建输入数据 Pipeline
dataset1 = dataset1 \
   .shuffle(1024) \
   .batch(32) \
   .prefetch(tf.data.experimential.AUTOTUNE)

for example in dataset1.take(1):
   image, label = example["image"], example["label"]

Note

  • 对于特别巨大而无法完整载入内存的数据集,可以先将数据集处理为 TFRecord 格式, 然后使用 tf.data.TFRecordDataset() 进行载入

2.3 TensorFlow 内置 Dataset

2.3.1 查看可用的数据集
import tensorflow as tf
import tensorflow_datasets as tfds

# 所有可用的数据集
print(tfds.list_builders())
2.3.2 内置数据集分类
  • Audio
    • groove
    • nsynth
  • Image
    • abstract_reasoning
    • aflw2k3d
    • bigearthnet
    • binarized_mnist
    • binaryalphadigits
    • caltech101
    • caltech_birds2010
    • caltech_birds2011
    • catsvsdogs
    • celeb_a
    • celebahq
    • cifar10
    • cifar100
    • cifar10_corrupted
    • clevr
    • coco
    • coco2014
    • coil100
    • colorectal_histology
    • colorectalhistologylarge
    • curatedbreastimaging_ddsm
    • cycle_gan
    • deep_weeds
    • diabeticretinopathydetection
    • downsampled_imagenet
    • dsprites
    • dtd
    • emnist
    • eurosat
    • fashion_mnist
    • food101
    • horsesorhumans
    • imagelabelfolder
    • imagenet2012
    • imagenet2012_corrupted
    • kitti
    • kmnist
    • lfw
    • lsun
    • mnist
    • mnist_corrupted
    • omniglot
    • openimagesv4
    • oxford_flowers102
    • oxfordiiitpet
    • patch_camelyon
    • pet_finder
    • quickdraw_bitmap
    • resisc45
    • rockpaperscissors
    • scene_parse150
    • shapes3d
    • smallnorb
    • so2sat
    • stanford_dogs
    • stanfordonlineproducts
    • sun397
    • svhn_cropped
    • tf_flowers
    • uc_merced
    • visualdomaindecathlon
    • voc2007
  • Structured
    • amazonusreviews
    • higgs
    • iris
    • rock_you
    • titanic
  • Text
    • cnn_dailymail
    • definitepronounresolution
    • gap
    • glue
    • imdb_reviews
    • lm1b
    • multi_nli
    • snli
    • squad
    • super_glue
    • trivia_qa
    • wikipedia
    • xnli
  • Translate
    • flores
    • para_crawl
    • tedhrlrtranslate
    • tedmultitranslate
    • wmt14_translate
    • wmt15_translate
    • wmt16_translate
    • wmt17_translate
    • wmt18_translate
    • wmt19_translate
    • wmtt2ttranslate
  • Video
    • bairrobotpushing_small
    • moving_mnist
    • starcraft_video
    • ucf101
2.3.3 构建并加载内置数据集
  • tfds.load 是构建并加载 tf.data.Dataset 最简单的方式
  • tf.data.Dataset 是构建输入流水线的标准 TensorFlow 接口
  • 加载数据集时,默认使用规范的版本,但是可以指定要使用的数据集的主版本,并在结果中表明使用了哪个版本的数据集

示例1:

mnist_train = tfds.load("mnist", split = "train", download = False, data_dir = "~/.tensorflow_datasets/")
assert isinstance(mnist_train, tf.data.Dataset)
print(mnist_train)

示例2:版本控制

mnist = tfds.load("mnist:1.*.*")
2.3.4 内置数据集特征字典
  • 所有 tensorflow_datasets, tfds 数据集都包含将特征名称映射到 Tensor 值的特征字典。典型的数据集将具有 2 个键:

    • "image"
    • "label"

示例:

mnist_train = tfds.load("mnist", split = "train", download = False, data_dir = "~/.tensorflow_datasets/")
for mnist_example in mnist_train.take(1):
   image, label = mnist_example["image"], mnist_example["label"]
   plt.imshow(
      image.numpy()[:, :, 0].astype(np.float32),
      cma = plt.get_cmap("gray")
   )
   print("Label: %d" % label.numpy())
   plt.show()
2.3.5 DatasetBuilder
  • tensorflow_datasets.load 实际上是一个基于 DatasetBuilder 的简单方便的包装器

示例:

mnist_builder = tfds.builder("mnist")
mnsit_builder.download_and_prepare()
mnist_train = mnist_builder.as_dataset(split = "train")
mnist_train
2.3.6 内置数据集输入流水线
  • 一旦有了 tf.data.Dataset 对象,就可以使用 tf.data 接口定义适合模型训练的输入流水线的其余部分.

示例:

mnist_train = mnist_train.repeat().shuffle(1024).batch(32)

# prefetch 将使输入流水线可以在模型训练时一步获取批处理
mnist_train = mnist_train \
               .repeat() \
               .shuffle(1024) \
               .batch(32) \
               .prefetch(tf.data.experimental.AUTOTUNE)
2.3.7 内置数据集信息

示例:

# method 1
info = mnist_builder.info
print(info)
print(info.features)
print(info.features["label"].num_classes)
print(info.features["label"].names)

# method 2
mnist_test, info = tfds.load("mnist", split = "test", with_info = True)
print(info)
2.3.9 内置数据集可视化

示例:

fig = tfds.show_examples(info, mnist_test)

3.TensorFlow Dataset 预处理数据

3.1 数据集预处理

4.1.1 数据集预处理 API 介绍
  • tf.data.Dataset 类提供了多种数据集预处理方法:
    • tf.data.Dataset.map(f):
      • 对数据集中的每个元素应用函数 f,得到一个新的数据集
      • 结合 tf.io 对文件进行读写和解码
      • 结合 tf.image 进行图像处理
    • tf.data.Dataset.shuffle(buffer_size):
      • 将数据集打乱
      • 设定一个固定大小的缓冲区(buffer),取出前 buffer_size 个元素放入,并从缓冲区中随机采样,采样后的数据用后续数据替换
    • tf.data.Dataset.batch(batch_size):
      • 将数据集分成批次
      • 对每 batch_size 个元素,使用 tf.stack() 在第 0 维合并,成为一个元素
    • tf.data.Dataset.repeat():
      • 重复数据集的元素
    • tf.data.Dataset.reduce():
      • 与 Map 相对的聚合操作
    • tf.data.Dataset.take():
      • 截取数据集中的前若干个元素
  • tf.data.Dataset.prefetch():
    • 并行化策略提高训练流程效率
  • 获取与使用 tf.data.Dataset 数据集元素
    • tf.data.Dataset 是一个 Python 的可迭代对象
4.1.2 数据集处理示例
  • (1)使用 tf.data.Dataset.map() 将所有图片旋转 90 度

    import tensorflow as tf
    
    # data preprocessing function
    def rot90(image, label):
       image = tf.image.rot90(image)
       return image, label
    
    # data
    mnist_dataset = tf.keras.datasets.mnist.load_data()
    
    # data preprocessing
    mnist_dataset = mnist_dataset.map(rot90)
    
    # data visual
    for image, label in mnist_dataset:
       plt.title(label.numpy())
       plt.imshow(image.numpy()[:, :, 0])
       plt.show()
    
  • (2)使用 tf.data.Dataset.batch() 将数据集划分为批次,每个批次的大小为 4

    import tensorflow as tf
    
    # data
    mnist_dataset = tf.keras.datasets.mnist.load_data()
    
    # data preprocessing
    mnist_dataset = mnist_dataset.batch(4)
    
    # data visual
    for images, labels in mnist_dataset: # image: [4, 28, 28, 1], labels: [4]
       fig, axs = plt.subplots(1, 4)
       for i in range(4):
          axs[i].set_title(label.numpy()[i])
          axs[i].imshow(images.numpy()[i, :, :, 0])
       plt.show()
    
  • (3)使用 tf.data.Dataset.shuffle() 将数据打散后再设置批次,缓存大小设置为 10000

    import tensorflow as tf
    
    # data
    mnist_dataset = tf.keras.datasets.mnist.load_data()
    
    # data preprocessing
    mnist_dataset = mnist_dataset.shuffle(buffer_size = 10000).batch(4)
    
    # data visual
    for i in range(2):
       for images, labels in mnist_dataset: # image: [4, 28, 28, 1], labels: [4]
          fig, axs = plt.subplots(1, 4)
          for i in range(4):
             axs[i].set_title(label.numpy()[i])
             axs[i].imshow(images.numpy()[i, :, :, 0])
          plt.show()
    

    Note

    • 一般而言,若数据集的顺序分布较为随机,则缓冲区的大小可较小,否则需要设置较大的缓冲区
  • (4)使用 tf.data.Dataset.prefetch() 并行化策略提高训练流程效率

    • 常规的训练流程
      • 当训练模型时,希望充分利用计算资源,减少 CPU/GPU 的空载时间,然而,有时数据集的准备处理非常耗时,
        使得在每进行一次训练前都需要花费大量的时间准备带训练的数据,GPU 只能空载等待数据,造成了计算资源的浪费
    • 使用 tf.data.Dataset.prefetch() 方法进行数据预加载后的训练流程
      • tf.data.Dataset.prefetch() 可以让数据集对象 Dataset 在训练时预先取出若干个元素,
        使得在 GPU 训练的同时 CPU 可以准备数据,从而提升训练流程的效率
    import tensorflow as tf
    
    # data preprocessing function
    def rot90(image, label):
       image = tf.image.rot90(image)
       return image, label
    
    # data
    mnist_dataset = tf.keras.datasets.mnist.load_data()
    
    # data preprocessing
    # 开启数据预加载功能
    mnist_dataset = mnist_dataset.prefetch(buffer_size = tf.data.experimental.AUTOTUNE)
    # 利用多 GPU 资源,并行化地对数据进行变换
    mnist_dataset = mnist_dataset.map(map_func = rot90, num_parallel_calls = 2)
    mnist_dataset = mnist_dataset.map(map_func = rot90, num_parallel_calls = tf.data.experimental.AUTOTUNE)
    
  • (5)获取与使用 tf.data.Dataset 数据集元素

    • 构建好数据并预处理后,需要从中迭代获取数据用于训练
    dataset = tf.data.Dataset.from_tensor_slices((A, B, C, ...))
    for a, b, c ... in dataset:
       pass
    
    dataset = tf.data.Dataset.from_tensor_slices((A, B, C, ...))
    it = iter(dataset)
    a_0, b_0, c_0, ... = next(it)
    a_1, b_1, c_1, ... = next(it)
    

4.2 图像

4.3 文本

tf.data.TextLineDataset 通常被用来以文本文件构建数据集(原文件中的一行为一个样本)。 这适用于大多数的基于行的文本数据(例如,诗歌或错误日志)。

  • 删除文档的页眉、页脚、行号、章节标题
import os
import tensorflow as tf
import tensorflow_datasets as tfds

DIRECTORY_URL = "https://storage.googleapis.com/download.tensorflow.org/data/illiad/"
FILE_NAMES = ["cowper.txt", "derby.txt", "butler.txt"]
for name in FILE_NAMES:
   text_dir = tf.keras.utils.get_file(name, origin = DIRECTORY_URL + name)

def labeler(example, index):
   return example, tf.cast(index, tf.int64)

parent_dir = os.path.dirname(text_dir)
labeled_data_sets = []
for i, file_name in enumerate(FILE_NAMES):
   lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name))
   labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i))
   labeled_data_sets.append(labeled_dataset)

BUFFER_SIZE = 50000
BATCH_SIZE = 64
TAKE_SIZE = 5000

all_labeled_data = labeled_data_sets[0]
for labeled_dataset in labeled_data_sets[1:]:
   all_labeled_data = all_labeled_data.concatenate(labeled_dataset)

all_labeled_data = all_labeled_data.shuffle(BUFFER_SIZE, reshuffle_each_iteration = False)

for ex in all_labeled_data.take(5):
   print(ex)

4.4 CSV

4.5 Numpy

4.6 pandas.DataFrame

4.7 Unicode

4.8 TF.Text

4.9 TFRecord

4.9.1 TFRecord 数据文件介绍

TFRecord 是 TensorFlow 中的数据集存储格式。当将数据集整理成 TFRecord 格式后, TensorFlow 就可以高效地读取和处理这些数据集了。从而帮助更高效地进行大规模模型训练。

TFRecord 可以理解为一系列序列化的 tf.train.Example 元素所组成的列表文件, 而每一个 tf.train.Example 又由若干个 tf.train.Feature 的字典组成:

# dataset.tfrecords
[
   {  # example 1 (tf.train.Example)
      'feature_1': tf.train.Feature,
      ...
      'feature_k': tf.train.Feature,
   },
   ...
   {  # example N (tf.train.Example)
      'feature_1': tf.train.Feature,
      ...
      'feature_k': tf.train.Feature,
   },
]
4.9.2 TFRecord 文件保存
  • TFRecord 文件保存步骤

    为了将形式各样的数据集整理为 TFRecord 格式,可以对数据集中的每个元素进行以下步骤:

      1. 读取该数据元素到内存
      1. 将该元素转换为 tf.train.Example 对象
      • 每个 tf.train.Example 对象由若干个 tf.train.Feature 的字典组成,因此需要先建立 Feature 的子典
      1. tf.train.Example 对象序列化为字符串,并通过一个预先定义的 tf.io.TFRecordWriter 写入 TFRecord 文件
  • TFRecord 文件保存示例

    import tensorflow as tf
    import os
    
    # root
    root_dir = "/Users/zfwang/project/machinelearning/deeplearning"
    # project
    project_path = os.path.join(root_dir, "deeplearning/src/tensorflow_src")
    # model save
    models_path = os.path.join(project_path, "save")
    # data
    cats_and_dogs_dir = os.path.join(root_dir, "datasets/cats_vs_dogs")
    data_dir = os.path.join(root_dir, "datasets/cats_vs_dogs/cats_and_dogs_small")
    # train data
    train_dir = os.path.join(data_dir, "train")
    train_cats_dir = os.path.join(train_dir, "cat")
    train_dogs_dir = os.path.join(train_dir, "dog")
    # tfrecord
    tfrecord_file = os.path.join(cats_and_dogs_dir, "train.tfrecord")
    
    # 训练数据
    train_cat_filenames = [os.path.join(train_cats_dir, filename) for filename in os.listdir(train_cats_dir)]
    train_dog_filenames = [os.path.join(train_dogs_dir, filename) for filename in os.listdir(train_dogs_dir)]
    train_filenames = train_cat_filenames + train_dog_filenames
    train_labels = [0] * len(train_cat_filenames) + [1] * len(train_dog_filenames)
    
    # 迭代读取每张图片,建立 tf.train.Feature 字典和 tf.train.Example 对象,序列化并写入 TFRecord
    with tf.io.TFRecordWriter(tfrecord_file) as writer:
       for filename, label in zip(train_filenames, train_labels):
          # 读取数据集图片到内存,image 为一个 Byte 类型的字符串
          image = open(filename, "rb").read()
          # 建立 tf.train.Feature 字典
          feature = {
                # 图片是一个 Byte 对象
                "image": tf.train.Feature(bytes_list = tf.train.BytesList(value = [image])),
                "label": tf.train.Feature(int64_list = tf.train.Int64List(value = [label]))
          }
          # 通过字典建立 Example
          example = tf.train.Example(features = tf.train.Features(feature = feature))
          # 将 Example 序列化并写入 TFRecord 文件
          writer.write(example.SerializeToString())
    
4.9.3 TFRecord 文件读取
  • TFRecord 数据文件读取步骤

    • (1)通过 tf.data.TFRecordDataset 读入原始的 TFRecord 文件,获得一个 tf.data.Dataset 数据集对象

      • 此时文件中的 tf.train.Example 对象尚未被反序列化
    • (2)通过 tf.data.Dataset.map 方法,对该数据集对象中的每个序列化的 tf.train.Example 字符串 执行 tf.io.parse_single_example 函数,从而实现反序列化

  • TFRecord 数据文件读取示例

    import tensorflow as tf
    import os
    import matplotlib.pyplot as plt
    
    # root
    root_dir = "/Users/zfwang/project/machinelearning/deeplearning"
    # data
    cats_and_dogs_dir = os.path.join(root_dir, "datasets/cats_vs_dogs")
    # tfrecord
    tfrecord_file = os.path.join(cats_and_dogs_dir, "train.tfrecord")
    
    def _parse_example(example_string):
       """
       将 TFRecord 文件中的每一个序列化的 tf.train.Example 解码
       """
       # 定义 Feature 结构,告诉解码器每个 Feature 的类型是什么
       feature_description = {
          "image": tf.io.FixedLenFeature([], tf.string),
          "label": tf.io.FixedLenFeature([], tf.int64)
       }
       feature_dict = tf.io.parse_single_example(example_string, feature_description)
       # 解码 JPEG 图片
       feature_dict["image"] = tf.io.decode_jpeg(feature_dict["image"])
       return feature_dict["image"], feature_dict["label"]
    
    # 读取 TFRecord 文件
    raw_dataset = tf.data.TFRecordDataset(tfrecord_file)
    dataset = raw_dataset.map(_parse_example)
    
    for image, label in dataset:
       plt.title("cat" if label == 0 else "dog")
       plt.imshow(image.numpy())
       plt.show()
    

4.10 tf.io 的其他格式

4.11 tf.TensorArray

4.11.1 tf.TensorArray 介绍

在部分网络结构中,尤其是涉及时间序列的结构中,可能需要将一系列张量以数组的方式依次存放起来,以供进一步处理。

  • 在即时执行模式下,可以直接使用一个 Python 列表存放数组
  • 如果需要基于计算图的特性,例如使用 @tf.function 加速模型运行或者使用 SaveModel 导出模型,就无法使用 Python 列表了

TensorFlow 提供了 tf.TensorArray (TensorFlow 动态数组) 支持计算图特性的 TensorFlow 动态数组.

  • 声明方式如下:

    • arr = tf.TensorArray(dtype, size, dynamic_size = False):

      • 声明一个大小为 size,类型为 dtypeTensorArray arr
      • 如果将 dynamic_size 参数设置为 True,则该数组会自动增长空间
  • 读取和写入的方法如下:

    • write(index, value): 将 value 写入数组的第 index 个位置
    • read(index): 读取数组的第 index 个值
    • stack()
    • unstack()
4.11.2 tf.TensorArray 介绍
import tensorflow as tf

@tf.function
def array_write_and_read():
   arr = tf.TensorArray(dtype = tf.float32, size = 3)
   arr = arr.write(0, tf.constant(0.0))
   arr = arr.write(1, tf.constant(1.0))
   arr = arr.write(2, tf.constant(2.0))
   arr_0 = arr.read(0)
   arr_1 = arr.read(1)
   arr_2 = arr.read(2)
   return arr_0, arr_1, arr_2

a, b, c = array_write_and_read()
print(a, b, c)

Note

  • 由于需要支持计算图,tf.TensorArraywrite() 是不可以忽略左值的, 也就是说,在图执行模式下,必须按照以下的形式写入数组,才可以正常生成一个计算图操作, 并将该操作返回给 arr:

    arr.write(index, value)
    
  • 不可以写成

    arr.write(index, value)
    

5.数据输入流水线

5.1 tf.data

5.2 优化流水线性能

5.3 分析流水线性能

TensorFlow TensorBoard

1.实时查看参数变化情况

1.1 TensorBoard 使用介绍

1.首先,在代码目录下建立一个文件夹,存放 TensorBoard 的记录文件

$ mkdir tensorboard

2.在代码中实例化一个记录器

summary_writer =  tf.summary.create_file_writer("./tensorboard")

3.当需要记录训练过程中的参数时,通过 with 语句指定希望使用的记录器,并对需要记录的参数(一般是标量)运行:

with summary_writer.as_default():
   tf.summary.scalar(name, tensor, step = batch_index)

4.当要对训练过程可视化时,在代码目录打开终端

$ tensorboard --logdir=./tensorboard

5.使用浏览器访问命令行程序所输出的网址, 即可访问 TensorBoard 的可视化界面

  • http://计算机名称:6006

Note

  • 每运行一次 tf.summary.scalar(),记录器就会向记录文件中写入一条记录
  • 除了最简单的标量以外,TensorBoard 还可以对其他类型的数据,如:图像、音频等进行可视化
  • 默认情况下,TensorBoard 每 30 秒更新一次数据,可以点击右上角的刷新按钮手动刷新
  • TensorBoard 的使用有以下注意事项:
    • 如果需要重新训练,那么删除掉记录文件夹内的信息并重启 TensorBoard, 或者建立一个新的记录文件夹并开启 TensorBoard,将 --logdir 参数设置为新建里的文件夹
    • 记录文件夹目录许保持全英文

1.2 TensorBoard 代码框架

# (1)实例化一个记录器
summary_writer =  tf.summary.create_file_writer("./tensorboard")

# (2)开始训练模型
for batch_index in range(num_batches):
   # ...(训练代码,将当前 batch 的损失值放入变量 loss 中)

   # (3)指定记录器
   with summary_writer.as_default():
      tf.summary.scalar("loss", loss, step = batch_index)
      tf.summary.scalar("MyScalar", my_scalar, step = batch_index)

2.查看 Graph 和 Profile 信息

在训练时使用 tf.summary.trace_on 开启 Trace,此时 TensorFlow 会将训练时的大量信息, 如:计算图的结构、每个操作所耗费的时间等,记录下来。

在训练完成后,使用 tf.summary.trace_export 将记录结果输出到文件。

1.使用 TensorBoard 代码框架对模型信息进行跟踪记录

# (1)实例化一个记录器
summary_writer =  tf.summary.create_file_writer("./tensorboard")

# (2)开启 Trace, 可以记录图结构和 profile 信息
tf.summary.trace_on(graph = True, profiler = True)

# (3)开始训练模型
for batch_index in range(num_batches):
   # (4)...(训练代码,将当前 batch 的损失值放入变量 loss 中)

   # (5)指定记录器, 将当前指标值写入记录器
   with summary_writer.as_default():
      tf.summary.scalar("loss", loss, step = batch_index)
      tf.summary.scalar("MyScalar", my_scalar, step = batch_index)

# (6)保存 Trace 信息到文件
with summary_writer.as_default():
   tf.summary.trace_export(name = "model_trace", step = 0, profiler_outdir = log_dir)

2.在 TensorBoard 的菜单中选择 PROFILE,以时间轴方式查看各操作的耗时情况, 如果使用了 @tf.function 建立计算图,也可以点击 GRAPHS 查看图结构

TensorFlow Estimator

  • 一种可极大地简化机器学习编程的高阶TensorFlow API;
  • Estimator封装的操作:
    • 训练
    • 评估
    • 预测
    • 导出以使用
  • Estimator优势:
    • 可以在本地主机上或分布式多服务器环境中运行基于 Estimator 的模型,而无需更改模型。此外,可以在 CPU、GPU 或 TPU 上运行基于 Estimator 的模型,而无需重新编码模型
    • Estimator 简化了在模型开发者之间共享实现的过程
    • 可以使用高级直观代码开发先进的模型。简言之,采用 Estimator 创建模型通常比采用低阶 TensorFlow API 更简单
    • Estimator 本身在 tf.layers 之上构建而成,可以简化自定义过程
    • Estimator 会为您构建图
    • Estimator 提供安全的分布式训练循环,可以控制如何以及何时:
      • 构建图
      • 初始化变量
      • 开始排队
      • 处理异常
      • 创建检查点文件并从故障中恢复
      • 保存 TensorBoard 的摘要

1.预创建的Estimator

预创建的 Estimator 程序的结构

依赖预创建的Estimator的TensorFlow程序通常包含下列四个步骤:

  1. 编写一个或多个数据集导入函数
    • 创建一个函数来导入训练集,并创建另一个函数来导入测试集。每个数据集导入函数都必须返回两个对象:
      • 一个字典,其中键是特征名称,值是包含相应特征数据的张量(or Sparse Tensro);
      • 一个包含一个或多个标签的张量;
  2. 定义特征列
    • 每个tf.feature_column都标识了特征名称、特征类型和任何输入预处理操作
  3. 实例化相关的预创建的Estimator
    • LinearClassifier
  4. 调用训练、评估或推理方法
    • 所有Estimator都提供训练模型的train方法

上面步骤实现举例:

def input_fn_train(dataset):
    # manipulate dataset, extracting the feature dict and the label

    return feature_dict, label

def input_fn_test(dataset):
    # manipulate dataset, extracting the feature dict and the label

    return feature_dict, label


my_training_set = input_fn_train()
my_testing_set = input_fn_test()

population = tf.feature_column.numeric_column('population')
crime_rate = tf.feature_column.numeric_column('crime_rate')
median_education = tf.feature_column.numeric_column('median_education',
                                                    normalizer_fn = lambda x: x - global_education_mean)

estimator = tf.estimator.LinearClassifier(
    feature_columns = [population, crime_rate, median_education],
)

estimator.train(input_fn = my_training_set, setps = 2000)

预创建的 Estimator 的优势

  • 预创建的 Estimator 会编码最佳做法,从而具有下列优势:
    • 确定计算图不同部分的运行位置以及在单台机器或多台机器上实现策略的最佳做法。
    • 事件(汇总)编写和普遍有用的汇总的最佳做法。

2.自定义的Estimator

  • 每个 Estimator(无论是预创建还是自定义)的核心都是其模型函数,这是一种为训练、评估和预测构建图的方法。如果您使用预创建的 Estimator,则有人已经实现了模型函数。如果您使用自定义 Estimator,则必须自行编写模型函数。
  • 推荐的工作流程:
    • 1.假设存在合适的预创建的Estimator,使用它构建第一个模型并使用其结果确定基准;
    • 2.使用此预创建的Estimator构建和测试整体管道,包括数据的完整性和可靠性;
    • 3.如果存在其他合适的预创建的Estimator,则运行试验来确定哪个预创建的Estimator效果好;
    • 4.可以通过构建自定义的Estimator进一步改进模型;

3.从 Keras 模型创建 Estimator

  • 可以将现有的Keras的模型转换为Estimator,这样Keras模型就可以利用Estimator的优势,比如进行分布式训练;
keras_inception_v3 = tf.keras.applications.keras_inception_v3.InceptionV3(weights = None)

keras_inception_v3.compile(optimizer = tf.keras.optimizers.SGD(lr = 0.0001, momentum = 0.9),
                           loss = 'categorical_crossentropy',
                           metric = 'accuracy')

est_inception_v3 = tf.keras.estimator.model_to_estimator(keras_model = keras_inception_v3)

keras_inception_v3.input_names

train_input_fn = tf.compat.v1.estimator.inputs.numpy_input_fn(
    x = {'input_1': train_data},
    y = train_labels,
    num_epochs = 1,
    shuffle = False
)

est_inception_v3.train(input_fn = train_input_fn, steps = 2000)

API:

从一个给定的Keras模型中构造一个Estimator实例
tf.keras.estimator.model_to_estimator(
    keras_model = None,
    keras_model_path = None,
    custom_objects = None,
    model_dir = None,
    config = None
)

TensorFlow SaveModel

为了将训练好的机器学习模型部署到各个目标平台(如服务器、移动端、嵌入式设备和浏览器等), 我们的第一步往往是将训练好的整个模型完整导出(序列化)为一系列标准格式的文件。在此基础上, 我们才可以在不同的平台上使用相对应的部署工具来部署模型文件。

TensorFlow 提供了统一模型导出格式 SaveModel, 这是我们在 TensorFlow 2 中主要使用的导出格式。 这样我们可以以这一格式为中介,将训练好的模型部署到多种平台上.

同时,基于历史原因,Keras 的 Sequential 和 Functional 模式也有自有的模型导出格式。

1.tf.train.Checkpoint: 变量的保存与恢复

很多时候,希望在模型训练完成后能将训练好的参数(变量)保存起来,这样在需要使用模型的其他地方载入模型和参数, 就能直接得到训练好的模型,保存模型有很多中方式:

  • Python 的序列化模块 pickle 存储 model.variables

    • 然而,TensorFlow 的变量类型 ResourceVariable 并不能被序列化
    • 语法:
    import pickle
    

1.1 tf.train.Checkpoint 介绍

  • tf.train.Checkpoint 简介

    TensorFlow 提供了 tf.train.Checkpoint 这一强大的变量保存与恢复类,提供的方法可以保存和恢复 TensorFlow 中的大部分对象, 比如下面类的实例都可以被保存:

    • tf.keras.optimizer
    • tf.Variable
    • tf.keras.Layer
    • tf.keras.Model
    • Checkpointable State 的对象
  • tf.train.Checkpoint 使用方法

    • 方法:

      • save()
      • restore()
    • 语法:

      # 保存训练好的模型, 先声明一个 Checkpoint
      model = TrainedModel()
      checkpoint = tf.train.Checkpoint(myAwesomeModel = model, myAwesomeOptimizer = optimizer)
      checkpoint.save(save_path_with_prefix)
      
      # 载入保存的训练模型
      model_to_be_restored = MyModel()  # 待恢复参数的同一模型
      checkpoint = tf.train.Checkpoint(myAwesomeModel = model_to_be_restored)
      checkpoint.restore(save_path_with_prefix_and_index)
      
      # 为了载入最近的一个模型文件, 返回目录下最近一次检查点的文件名
      tf.train.latest_checkpoint(save_path)
      

Note

  • 参数:

    • myAwesomeModel: 待保存的模型 model 所取的任意键名,在恢复变量时还将使用这一键名
    • myAwesomeOptimizer: 待保存的模型 optimizer 所取的任意键名,在恢复变量时还将使用这一键名
    • save_path_with_prefix: 保存文件的目录+前缀
    • save_path_with_prefix_and_index: 之前保存的文件目录+前缀+序号
  • checkpoint.save("./model_save/model.ckpt"): 会在模型保存的文件夹中生成三个文件:

    • checkpoint
    • model.ckpt-1.index
    • model.ckpt-1.data-00000-of-00001
  • checkpoint.restore("./model/save/model.ckpt-1")

    • 载入前缀为 model.ckpt、序号为 1 的文件来恢复模型

1.2 tf.train.Checkpoint 代码框架

1.train.py 模型训练阶段

# 训练好的模型
model = MyModel()

# 实例化 Checkpoint, 指定保存对象为 model(如果需要保存 Optimizer 的参数也可以加入)
checkpoint = tf.train.Checkpoint(myModel = model)
manager = tf.train.CheckpointManager(checkpoint, directory = "./save", checkpoint_name = "model.ckpt", max_to_keep = 10)

# ...(模型训练代码)

# 模型训练完毕后将参数保存到文件(也可以在模型训练过程中每隔一段时间就保存一次)
if manager:
    manager.save(checkpoint_number = 100)
else:
    checkpoint.save("./save/model.ckpt")

2.test.py 模型使用阶段

# 要使用的模型
model = MyModel()

# 实例化 Checkpoint, 指定恢复对象为 model
checkpoint = tf.train.Checkpoint(myModel = model)

# 从文件恢复模型参数
checkpoint.restore(tf.train.latest_checkpoint("./save))

# ...(模型使用代码)

Note

  • tf.train.Checkpoint (检查点)只保存模型的参数,不保存模型的计算过程, 因此一般用于在具有的模型源码时恢复之前训练好的模型参数。如果需要导出模型(无须源代码也能运行模型)。

2.使用 SaveModel 完整导出模型

作为模型导出格式的 SaveModel 包含了一个 TensorFlow 程序的完整信息: 不仅包含参数的权值,还包含计算的流程(计算图)。 当模型导出为 SaveModel 文件时,无须模型的源代码即可再次运行模型, 这使得 SaveModel 尤其适用于模型的分享和部署。

Keras 模型均可以方便地导出为 SaveModel 格式。不过需要注意的是,因为 SaveModel 基于计算图, 所以对于通过继承 tf.keras.Model 类建立的 Keras 模型来说,需要导出为 SaveModel 格式的方法(比如 call) 都需要 使用 @tf.function 修饰。

语法:

# 保存
tf.saved_model.save(model, "保存的目标文件夹名称")

# 载入
model = tf.saved_model.load("保存的目标文件夹名称")

示例:

pass

3.Keras 自有的模型导出格式

示例:

curl -LO https://raw.githubcontent.com/keras-team/keras/master/examples/mnist_cnn.py
model.save("mnist_cnn.h5")
import keras

keras.models.load_model("mnist_cnn.h5")

TensorFLow Serving

1.TensorFLow Serving 安装

2.TensorFLow Serving 模型部署

3.在客户端调用以 TensorFLow Serving 部署的模型

TensorFLow Serving 支持使用 gRPC 方法和 RESTful API 方法调用以 TensorFLow Serving 部署的模型。

RESTful API 以标准的 HTTP POST 方法进行交互,请求和回复均为 JSON 对象。为了调用服务器端的模型,在客户端向服务器发送以下格式的请求.

  • 服务器 URI: http://服务器地址:端口号/v1/models/模型名:predict

  • 请求内容

    {
        "signature_name": "需要调用的函数签名(Sequential模式不需要)",
        "instances": "输入数据"
    }
    
  • 回复:

    {
        "predictions": "返回值"
    }
    

TensorFlow Keras Pipeline

1.Keras Sequential/Functional API 模式建立模型

1.0 Keras Subclassing API

  • 使用 Keras 的 Subclassing API 建立模型,即对 tf.keras.Model 类进行扩展以定义自己的新模型

    import tensorflow as tf
    
    class MyModel(tf.keras.Model):
        def __init__(self):
            super.__init__()
            # 此处添加初始化的代码(包含call方法中会用到的层)例如:
            layer1 = tf.keras.layers.BuildInLayer()
            layer2 = MyCustomLayer(...)
    
        def call(self, input):
            # 此处添加模型调用的代码(处理输入并返回输出),例如:
            x = layer1(input)
            output = layer2(x)
            return output
    

1.1 Sequential

model = tf.keras.models.Sequential(
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(100, activation = tf.nn.relu),
    tf.keras.layers.Dense(10),
    tf.keras.layers.Softmax()
)

1.2 Functional API

inputs = tf.keras.Input(shape = (28, 28, 1))
x = tf.keras.layers.Flatten()(inputs)
x = tf.keras.layers.Dense(units = 100, activation = tf.nn.relu)(x)
x = tf.keras.layers.Dense(units = 10)(x)
outputs = tf.keras.layers.Softmax()(x)
model = tf.keras.Model(inputs = inputs, outputs = outputs)

2.Keras Model–compile、fit、evaluate

2.1 compile

model.compile(
    optimizer = tf.keras.optimizers.Adam(learning_rate = 0.001),
    loss = tf.keras.losses.sparse_categorical_crossentropy,
    metrics = [tf.keras.metrics.sparse_categorical_accuracy]
)

Note

  • tf.keras.Model.compile 3 个主要参数:

    • optimizer: 优化器,可从 tf.keras.optimizers 中选择
      • .Adam(learning_rate)
    • loss: 损失函数,可从 tf.keras.losses 中选择
      • .sparse_categorical_crossentropy
    • metrics: 评估指标,可从 tf.keras.metrics 中选择
      • .sparse_categorical_accuracy

2.2 fit

model.fit(
    x = data_loader.train_data,
    y = data_loader.train_label,
    epochs = num_epochs,
    batch_size = batch_size,
    validation_data = data_loader.validation_data
)

Note

  • tf.keras.Model.fit 5 个主要参数:

    • x: 训练数据
    • y: 训练数据目标数据(数据标签)
    • epochs: 将训练数据迭代多少遍
    • batch_size: 批次的大小
    • validation_data: 验证数据,可用于在训练过程中监控模型的性能

2.3 evaluate

print(model.evaluate(data_loader.test_data, data_loader.test_label))

Note

  • tf.keras.Model.evaluate 2 个参数:

    • x: 测试数据
    • y: 测试数据目标数据(数据标签)

3.自定义层、损失函数、评估指标

3.1 自定义层

  • 自定义层需要继承 tf.keras.layers.Layers 类,并重写 __init__buildcall 三个方法

    import numpy as np
    import tensorflow as tf
    
    class MyLayer(tf.keras.layers.Layer):
        def __init__(self):
            super().__init__()
            # 初始化代码
    
        def build(self, input_shape): # input_shape 是一个 TensorShape 类型对象,提供输入的形状
            # 在第一次使用该层的时候调用该部分代码,在这里创建变量可以使得变量的形状自适应输入的形状
            # 而不需要使用者额外指定变量形状
            # 如果已经可以完全确定变量的形状,也可以在 __init__ 部分创建变量
            self.variable_0 = self.add_weight(...)
            self.variable_1 = self.add_weight(...)
    
        def call(self, inputs):
            # 模型调用的代码(处理输入并返回输出)
            return output
    
  • 线性层示例

    import numpy as np
    import tensorflow as tf
    
    class LinearLayer(tf.keras.layers.Layer):
        def __init__(self, units):
            super.__init__()
            self.units = units
    
        def build(self, input_shape):
            self.w = self.add_variable(
                name = "w",
                shape = [input_shape[-1], self.units],  # [n, 1]
                initializer = tf.zeros_initializer()
            )
            self.b = self.add_variable(
                name = "b",
                shape = [self.units],                   # [1]
                initializer = tf.zeros_initializer()
            )
    
        def call(self, inputs):
            y_pred = tf.matmul(inputs, self.w) + self.b
            return y_pred
    
    class LinearModel(tf.keras.Model):
        def __init__(self):
            super().__init__()
            self.layer = LinearLayer(untis = 1)
    
        def call(self, inputs):
            output = self.layer(inputs)
            return output
    

3.2 自定义损失函数

  • 自定义损失函数需要继承 tf.keras.losses.Loss 类,重写 call 方法即可,输入真实值 y_true 和模型预测值 y_pred, 输出模型预测值和真实值之间通过自定义的损失函数计算出的损失值

    import numpy as np
    import tensorflow as tf
    
    class MeanSquaredError(tf.keras.losses.Loss):
        def call(self, y_true, y_pred):
            return tf.reduce_mean(tf.square(y_pred - y_true))
    

3.3 自定义评估指标

  • 自定义评估指标需要继承 tf.keras.metrics.Metric 类,并重写 __init__update_stateresult 三个方法

    import numpy as np
    import tensorflow as tf
    
    class SparseCategoricalAccuracy(tf.keras.metrics.Metric):
        def __init__(self):
            super().__init__()
            self.total = self.add_weight(name = "total", dtype = tf.int32, initializer = tf.zeros_initializer())
            self.count = self.add_weight(name = "total", dtype = tf.int32, initializer = tf.zeros_initializer())
    
        def update_state(self, y_true, y_pred, sample_weight = None):
            values = tf.cast(tf.equal(y_true, tf.argmax(y_pred, axis = 1, output_type = tf.int32)), tf.int32)
            self.total.assign_add(tf.shape(y_true)[0])
            self.count.assign_add(tf.reduce_sum(values))
    
        def result(self):
            return self.count / self.total
    

TensorFlow Performance

1.使用 tf.function 提升性能

1.1 @tf.funciton: 图执行模式

虽然目前 TensorFlow 默认的即时执行模式具有灵活及易调试的特性,但在特定的场合, 例如追求高性能或部署模型时,依然希望使用图执行模式,将模型转换为高效的 TensorFlow 图模型。

TensorFlow 2 提供了 tf.function 模块,结合 AutoGraph 机制,使得我们仅需加入一个简单的 @tf.function 修饰符,就能轻松将模型以图执行模式运行。

1.2 @tf.function 基础使用方法

@tf.function 的基础使用非常简单,只需要将我们希望以图执行模式运行的代码封装在一个函数内, 并在函数前面加上 @tf.function 即可.

pass

1.3 @tf.function 内在机制

1.4 AutoGraph: 将 Python 控制流转化为 TensorFlow 计算图

1.5 使用传统的 tf.Session

2.分析 TenforFlow 的性能

3.图优化

4.混合精度

Keras

1.Keras 介绍

  • Keras: The Python Deep Learning library

    • keras
    • tensorflow.keras
  • 为什么要使用 Keras?

    • Keras 优先考虑开发人员
    • Keras 已在业界和研究界广泛使用
    • Keras 使得将模型转化为产品变得容易
    • Keras 支持多种后端引擎
    • Keras 具有强大的多 GPU 支持和分布式训练支持
    • Keras 开发得到了深度学习生态系统中主要公司的支持

2.Keras 入门

  • Keras 核心数据结构:

    • tensorflow.keras.layers
    • tensorflow.keras.models
  • Keras Model 类型:

    • Sequential model
    • Keras functional API
    • Scratch via subclassing
  • Keras 模型实现:

    • 类 Scikit-Learn API 示例:

      from tensorflow import keras
      from tensorflow.keras import layers, models
      from tensorflow.keras.datasets import mnist
      
      (x_train, y_train), (x_test, y_test) = mnist.load_data()
      x_train, x_test = x_train / 255.0, x_test / 255.0
      model = models.Sequential()
      model.add(layers.Dense(units = 64, activation = "relu"))
      model.add(layers.Dense(units = 10, activation = "softmax"))
      model.compile(
         loss = "categorical_crossentropy",
         optimizer = "sgd",
         metrics = ["accuracy"]
      )
      # model.compile(
      #     loss = keras.losses.categorical_crossentropy,
      #     optimizer = keras.optimizers.SGD(learning_rate = 0.01, momentum = 0.9, nesterov = True)
      # )
      model.fit(x_train, y_train, epochs = 5, batch_size = 32)
      loss_and_metrics = model.evaluate(x_test, y_test, batch_size = 128)
      classes = model.predict(x_test, batch_size = 128)
      
    • 低级循环训练示例:

      import tensorflow as tf
      
      # prepare an optimizer.
      optimizer = tf.keras.optimizers.Adam()
      # prepare a loss function.
      loss_fn = tf.keras.losses.kl_divergence
      
      # Iterate over the batches of a dataset.
      for inputs, targets in dataset:
         # Open a GradientTape
         with tf.GradientTape() as tape:
            # Forward pass.
            predictions = model(inputs)
            # Compute the loss value for this batch.
            loss_value = loss_fn(targets, predictions)
      
         # Get gradients of loss wrt the weights.
         gradients = tape.gradient(loss_value, model.trainable_weights)
         # Update the weights of the model
         optimizer.apply_gradients(zip(gradients, model.trainable_weights))
      

Keras Pipeline

import numpy as np
import tensorflow as tf
from tensorflow import keras

介绍

  • How to prepare you data before training a model (by turning it into either NumPy arrays or tf.data.Dataset objects).
  • How to do data preprocessing, for instance feature normalization or vocabulary indexing.
  • How to build a model that turns your data into useful predictions, using the Keras Functional API.
  • How to train your model with the built-in Keras fit() method, while being mindful of checkpointing, metrics monitoring, and fault tolerance.
  • How to evaluate your model on a test data and how to use it for inference on new data.
  • How to customize what fit() does, for instance to build a GAN.
  • How to speed up training by leveraging multiple GPUs.
  • How to refine your model through hyperparameter tuning.

Keras 后端

1.什么是 Keras 后端?

Keras 后端:

Keras 是一个模型级库, 为开发深度学习模型提供了高层次的构建模块。它不处理诸如张量乘积和卷积等低级操作。 相反, 它依赖于一个专门的、优化的张量操作库来完成这个操作, 它可以作为 Keras 的「后端引擎」。 相比单独地选择一个张量库, 而将 Keras 的实现与该库相关联, Keras 以模块方式处理这个问题, 并且可以将几个不同的后端引擎无缝嵌入到 Keras 中。

目前可用的 Keras 后端:

  • TensorFlow
  • Theano
  • CNTK

2.从一个后端切换到另一个后端

如果您至少运行过一次 Keras, 您将在以下位置找到 Keras 配置文件. 如果没有, 可以手动创建它.

Keras 配置文件位置:

# Liunx or Mac
$ vim $HOME/.keras/keras.json

# Windows
$ vim %USERPROFILE%/.keras/keras.json

Keras 配置文件创建:

$ cd ~/.keras
$ sudo subl keras.json

也可以定义环境变量 KERAS_BACKEND, 不过这会覆盖配置文件 $HOME/.keras/keras.json 中定义的内容:

KERAS_BACKEND=tensorflow python -c "from keras import backend"
Using TensorFlow backend.

当前环境的 Keras 配置文件内容:

{
   "floatx": "float32",
   "epsilon": 1e-07,
   "backend": "tensorflow",
   "image_data_format": "channels_last"
}

自定义 Keras 配置文件:

  • 在 Keras 中, 可以加载除了 “tensorflow”, “theano” 和 “cntk” 之外更多的后端。Keras 也可以使用外部后端, 这可以通过更改 keras.json 配置文件和 “backend” 设置来执行。 假设您有一个名为 my_module 的 Python 模块, 您希望将其用作外部后端。keras.json 配置文件将更改如下.

    • 必须验证外部后端才能使用, 有效的后端必须具有以下函数:

      • placeholder
      • variable
      • function
    • 如果由于缺少必需的条目而导致外部后端无效, 则会记录错误, 通知缺少哪些条目:

      {
         "image_data_format": "channels_last",
         "epsilon": 1e-07,
         "floatx": "float32",
         "backend": "my_package.my_module"
      }
      

3.keras.json 详细配置

  • image_data_format:
    • "channels_last"
      • (rows, cols, channels)
      • (conv*dim1, conv*dim2, conv_dim3, channels)
    • "channels_first"
      • (channels, rows, cols)
      • (channels, convdim1, convdim2, conv_dim3)
    • 在程序中返回: keras.backend.image_data_format()
  • epsilon:
    • 浮点数, 用于避免在某些操作中被零除的数字模糊常量
  • floatx:
    • 字符串: float16, float32, float64。默认浮点精度
  • backend:
    • 字符串: tensorflow, theano, cntk

4.使用抽象 Keras 后端编写新代码

如果你希望你编写的 Keras 模块与 Theano (th) 和 TensorFlow (tf) 兼容, 则必须通过抽象 Keras 后端 API 来编写它们。

from keras import backend as K
import numpy as np

# 实例化一个输入占位符
inputs = K.placeholder(shape = (2, 4, 5))
inputs = K.placeholder(shape = (None, 4, 5))
inputs = K.placeholder(ndim = 3)

# 实例化一个变量
val = np.random.random((3, 4, 5))
var = K.variable(value = val)
var = K.zeros(shape = (3, 4, 5))
var = K.ones(shape = (3, 4, 5))

等价于:

import tensorflow as tf

# 实例化一个输入占位符
inputs = tf.placeholder()
inputs = tf.tensor.matrix()
inputs = tf.tensor.tensor3()

# 实例化一个变量
var = tf.Variable()
var = tf.shared()

5.后端函数

  • keras.backend.backend()
    • Keras 目前正在使用的后端名
  • keras.backend.symbolic(func)
    • Decorator used in TensorFlow 2.0 to enter the Keras graph.
  • keras.backend.eager(func)
    • Decorator used in TensorFlow 2.0 to exit the Keras graph.
  • keras.backend.get_uids(prefix = "")
    • 获取默认计算图的标识符
    • prefix: 图的可选前缀
  • keras.backend.manual_variable_initialization(value)
    • 设置变量手动初始化标志
  • keras.backend.epsilon()
    • Returns the value of the fuzz factor used in numeric expressions.
  • keras.backend.reset_uids()
    • 重置图的标识符

6.Resets graph identifiers

  • keras.backend.set_epsilon(e)
  • keras.backend.floatx()
    • keras.backend.set_floatx()
    • keras.backend.cast_to_floatx()
  • keras.backend.image_data_format()
    • keras.backend.set_image_data_format(data_format)
  • keras.backend.learning_phase()
    • keras.backend.set_learning_phase()
  • keras.backend.clear_session()
    • 销毁当前的 TF 图并创建一个新图
    • 有用于避免旧模型/网络层混乱
  • 张量(Tensor)
    • keras.backend.is_sparse()
    • keras.backend.to_dense()
    • keras.backend.variable()
    • keras.backend.constant()
    • keras.backend.is_keras_tensor()
    • keras.backend.is_tensor()
    • keras.backend.placeholder()
    • keras.backend.is_placeholder()
    • keras.backend.shape()
    • keras.backend.int_shape()
    • keras.backend.ndim()
    • keras.backend.dtype()
    • keras.backend.eval()
    • keras.backend.zeros()
    • keras.backend.zeros_like()
    • keras.backend.ones()
    • keras.backend.ones_like()
    • keras.backend.eye()
    • keras.backend.identity()
    • keras.backend.random_uniform_variable()
    • keras.backend.random_normal_variable()
    • keras.backend.count_params()
    • keras.backend.cast()
    • keras.backend.update()
    • keras.backend.update_add()
    • keras.backend.update_sub()
    • keras.backend.moving_average_update()
    • keras.backend.batch_dot()
    • keras.backend.transpose()
    • keras.backend.gather()
    • keras.backend.max()
    • keras.backend.min()
    • keras.backend.sum()
    • keras.backend.prod()
    • keras.backend.cumsum()
    • keras.backend.cumprod()
    • keras.backend.var()
    • keras.backend.std()
    • keras.backend.mean()
    • keras.backend.any()
    • keras.backend.all()
    • keras.backend.argmax()
    • keras.backend.argmin()
    • keras.backend.square()
    • keras.backend.abs()
    • keras.backend.sqrt()
    • keras.backend.exp()
    • keras.backend.log()
    • keras.backend.logsumexp()
    • keras.backend.round()
    • keras.backend.sign()
    • keras.backend.pow()
    • keras.backend.clip()
    • keras.backend.equal()
    • keras.backend.not_equal()
    • keras.backend.greater()
    • keras.backend.greater_equal()
    • keras.backend.less()
    • keras.backend.less_equal()
    • keras.backend.maximum()
    • keras.backend.minimum()
    • keras.backend.sin()
    • keras.backend.cos()
    • keras.backend.normalize_batch_in_training()
    • keras.backend.batch_normalization()
    • keras.backend.concatenate()
    • keras.backend.reshape()
    • keras.backend.permute_dimensions()
    • keras.backend.resize_images()
    • keras.backend.resize_volumes()
    • keras.backend.repeat_elements()
    • keras.backend.repeat()
    • keras.backend.arange()
    • keras.backend.tile()
    • keras.backend.flatten()
    • keras.backend.batch_flatten()
    • keras.backend.expand_dims()
    • keras.backend.squeeze()
    • keras.backend.temporal_padding()
    • keras.backend.spatial_2d_padding()
    • keras.backend.spatial_3d_padding()
    • keras.backend.stack()
    • keras.backend.one_hot()
    • keras.backend.reverse()
    • keras.backend.slice()
    • keras.backend.get_value()
    • keras.backend.batch_get_value()
    • keras.backend.set_value()
    • keras.backend.batch_set_value()
    • keras.backend.print_tensor()
    • keras.backend.function()
    • keras.backend.gradients()
    • keras.backend.stop_gradient()
    • keras.backend.rnn()
    • keras.backend.switch()
    • keras.backend.in_train_phase()
    • keras.backend.in_test_phase()
    • keras.backend.relu()
    • keras.backend.elu()
    • keras.backend.softmax()
    • keras.backend.softplus()
    • keras.backend.softsign()
    • keras.backend.categorical_crossentropy()
    • keras.backend.sparse_categorical_crossentropy()
    • keras.backend.binary_crossentropy()
    • keras.backend.sigmoid()
    • keras.backend.hard_sigmoid()
    • keras.backend.tanh()
    • keras.backend.dropout()
    • keras.backend.l2_normalize()
    • keras.backend.in_top_k()
    • keras.backend.conv1d()
    • keras.backend.conv2d()
    • keras.backend.conv2d_transpose()
    • keras.backend.separable_conv1d()
    • keras.backend.separable_conv2d()
    • keras.backend.depthwise_conv2d()
    • keras.backend.conv3d()
    • keras.backend.conv3d_transpose()
    • keras.backend.pool2d()
    • keras.backend.pool3d()
    • keras.backend.bias_add()
    • keras.backend.random_normal()
    • keras.backend.random_uniform()
    • keras.backend.random_binomial()
    • keras.backend.truncated_normal()
    • keras.backend.ctc_label_dense_to_sparse()
    • keras.backend.ctc_batch_cost()
    • keras.backend.ctc_decode()
    • keras.backend.map_fn()
    • keras.backend.foldl()
    • keras.backend.foldr()
    • keras.backend.local_conv1d()
    • keras.backend.local_conv2d()

Keras 数据预处理

内容

  • Sequence Preprocessing
    • TimeseriesGenerator
    • pad_sequences
    • skipgrams
    • makesamplingtable
  • Text Preprocessing
    • Tokenizer
    • hashing_trick
      • 将文本转换为固定大小的散列空间中的索引序列
    • one_hot
      • One-hot将文本编码为大小为n的单词索引列表
    • texttoword_sequence
      • 将文本转换为单词(或标记)序列
  • Image Preprocessing
    • class ImageDataGenerator
      • method
        • .apply_transform()
        • .fit ()
        • .flow()
          • 采用数据和标签数组,生成批量增强数据
        • .flowfromdataframe()
          • 获取数据帧和目录路径,并生成批量的扩充/规范化数据
        • .flowfromdirectory()
          • 获取目录的路径并生成批量的增强数据
        • .getrandomtransform()
          • 为转换生成随机参数
        • .random_transform()
          • 随机转换
        • .standardize()
          • 标准化

1.Sequence Preprocessing

2.Text Preprocessing

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.text import hashing_trick
from keras.preprocessing.text import one_hot
from keras.preprocessing.text import text_to_word_sequence

3.Image Preprocessing

keras.preprocessing.imgae.ImageDataGenerator 通过实时数据增强生成批量张量图像数据:

keras.preprocessing.image.ImageDataGenerator(featurewise_center = False, # 将数据的特征均值设定为0
    samplewise_center = False,  # 将数据的样本均值设定为0
    featurewise_std_normalization = False, # 是否将特征除以特征的标准差进行归一化
    samplewise_std_normalization = False,  # 是否将样本除以样本的标准差进行归一化
    zca_whitening = False, # 是否进行 ZCA 白化
    zca_epsilon = 1e-06,   # 进行 ZCA 白化的epsilon参数
    rotation_range = 0,      # 随机旋转的角度范围
    width_shift_range = 0.0, # 宽度调整的范围
    height_shift_range = 0.0,# 高度调整的范围
    brightness_range = None, # 亮度范围
    shear_range = 0.0,         # 剪切范围
    zoom_range = 0.0,          # 缩放范围
    channel_shift_range = 0.0, # 通道调整范围
    fill_mode = 'nearest',     # 填充边界之外点的方式:
    cval=0.0,
    horizontal_flip=False,  # 水平翻转
    vertical_flip=False,    # 垂直翻转
    rescale=None,           #
    preprocessing_function=None,
    data_format=None,
    validation_split=0.0,
    dtype=None)

用法:

from keras.datasets import cifar10
from keras import utils
from keras.preprocessing.image import ImageDataGenerator

# model training parameters
num_classes = 10
data_augmentation = True
batch_size = 32
epochs = 20

# data
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype("float32")
x_test = x_test.astype("float32")
x_train /= 255
x_test /= 255
y_train = utils.to_categorical(y_train, num_classes = num_classes)
y_test = utils.to_categorical(y_test, num_classes = num_classes)

# model training
if not data_augmentation:
    print("Not using data augmentation.")
    model.fit(x_train, y_train,
              batch_size = batch_size,
              epochs = epochs,
              validation_data = (x_test, y_test),
              shuffle = True)
else:
    print("Using real-time data augmentation.")
    # This will do preprocessing and realtime data augmentation:
    datagen = ImageDataGenerator(
        featurewise_center = False,
        samplewise_center = False,
        featurewise_std_normalization = False,
        samplewise_std_normalization = False,
        zca_whitening = False,
        zca_epsilon = 1e-6,
        rotation_range = 0,
        width_shift_range = 0.1,
        height_shift_range = 0.1,
        shear_range = 0.,
        zoom_range = 0.,
        channel_shift_range = 0,
        fill_mode = "nearest",
        cval = 0.,
        horizontal_flip = True,
        vertical_flip = False,
        rescale = None,
        preprocessing_function = None,
        data_format = None,
        validation_split = 0.0
    )
    datagen.fit(x_train)
    model.fit_generator(datagen.flow(x_train,
                                     y_train,
                                     batch_size = batch_size,
                                     epochs = epochs,
                                     validation_data = (x_test, y_test),
                                     workers = 4))
from keras.datasets import cifar10
from keras import utils


# data
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype("float32")
x_test = x_test.astype("float32")
x_train /= 255
x_test /= 255
y_train = utils.to_categorical(y_train, num_classes = num_classes)
y_test = utils.to_categorical(y_test, num_classes = num_classes)


# model training parameters
batch_size = 32
epochs = 20
num_classes = 10
data_augmentation = True

# model training
datagen = ImageDataGenerator(featurewise_center = True,
                             featurewise_std_normalization = True,
                             rotation_range = 20,
                             width_shift_range = 0.2,
                             height_shift_range = 0.2,
                             horizontal_flip = True)

for e in range(epochs):
    print("Epoch", e)
    batches = 0
    for x_batch, y_batch in datagen.flow(x_train, y_train, batch_size = batch_size):
        model.fit(x_batchd, y_batch)
        batches += 1
        if batches >= len(x_train) / 32:
            break
train_datagen = ImageDataGenerator(rescale = 1. / 255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True)
test_datagen = ImageDataGenerator(rescale = 1.0 / 255)

train_generator = train_datagen \
    .flow_from_directory("data/train",
                         target_size = (150, 150),
                         batch_size = 32,
                         class_mode = "binary")
validation_generator = test_datagen \
    .flow_from_directory("data/validation",
                         target_size = (150, 150),
                         batch_size = 32,
                         class_mode = "binary")

model.fit_generator(train_generator,
                    steps_per_epoch = 2000,
                    epochs = 50,
                    validation_data = validation_generator,
                    validation_steps = 800)
# we create two instances with the same arguments
data_gen_args = dict(featurewise_center=True,
                     featurewise_std_normalization=True,
                     rotation_range=90,
                     width_shift_range=0.1,
                     height_shift_range=0.1,
                     zoom_range=0.2)
image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)

# Provide the same seed and keyword arguments to the fit and flow methods
seed = 1
image_datagen.fit(images, augment=True, seed=seed)
mask_datagen.fit(masks, augment=True, seed=seed)

image_generator = image_datagen.flow_from_directory(
    'data/images',
    class_mode=None,
    seed=seed)

mask_generator = mask_datagen.flow_from_directory(
    'data/masks',
    class_mode=None,
    seed=seed)

# combine generators into one which yields image and masks
train_generator = zip(image_generator, mask_generator)

model.fit_generator(
    train_generator,
    steps_per_epoch=2000,
    epochs=50)

keras Sequential model

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

1.什么时候使用 Sequential 模型

A Sequential model is appropriate for a plain stack of layers where each layer has exactly one input tensor and one output tensor.

A Sequential model is not appropriate when:

  • Your model has multiple inputs or multiple outputs
  • Any of your layers has multiple inputs or multiple outputs
  • You need to do layer sharing
  • You want non-linear topology (e.g. a residual connection, a multi-branch model)

2.构建一个 Sequential 模型

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential(name = "my_sequential")
model.add(layers.Dense(2, activation = "relu", name = "layer1"))
model.add(layers.Dense(3, activation = "relu", name = "layer2"))
model.add(layers.Dense(4, name = "layer3"))
print(model.layers)

# delete latest layer of the model
model.pop()
print(len(model.layers))

4.Sequential 模型的特征提取功能

5.Sequential 模型实现 Transfer images

Transfer learning consists of freezing the bottom layers in a model and only training the top layers. If you aren’t familiar with it, make sure to read our guide to transfer learning.

Here are two common transfer learning blueprint involving Sequential models.

  • First, let’s say that you have a Sequential model, and you want to freeze all layers except the last one. In this case, you would simply iterate over model. layers and set layer.trainable = False on each layer, except the last one. Like this:
# Sequential model
model = keras.Sequential()
model.add(keras.Input(shape = (784))
model.add(layers.Dense(32, activation = "relu"))
model.add(layers.Dense(32, activation = "relu"))
model.add(layers.Dense(32, activation = "relu"))
model.add(layers.Dense(10))

# Presumably you would want to first load pre-trained weights
model.load_weights(...)

# Freeze all layers except the last one
for layer in model.layers[:-1]:
    layer.trainable = False

# Recompile and train(this will only update the weights of the last layer).
model.compile(...)
model.fit(...)
  • Another common blueprint is to use a Sequential model to stack a pre-trained model and some freshly initialized classification layers. Like this:
# Load a convolutional base with pre-trained weights
base_model = keras.applications.Xception(
    weights = "imagenet",
    include_top = False,
    pooling = "avg"
)

# Freeze the base model
base_model.trainable = False

keras Functional API

API 使用总结:

  • keras.models.load_model
  • .save()

Functional API 的使用技巧:

  • 优势
  • 弱点

入门

库文件导入:

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

模型拓扑结构:

Functional API 模型:

>>> from tensorflow import keras
>>> from tensorflow.keras import layers, models
>>> inputs = keras.Input(shape = (784,))
>>> x = layers.Dense(64, activation = "relu")(inputs)
>>> x = layers.Dense(64, activation = "relu")(x)
>>> outputs = layers.Dense(10)(x)
>>> model = keras.Model(inputs = inputs, outputs = outputs, name = "mnist_model")
>>> model.summary()
Model: "mnist_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
input_1 (InputLayer)         [(None, 784)]             0
_________________________________________________________________
dense (Dense)                (None, 64)                50240
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________
>>> print(inputs.shape)
>>> print(inputs.dtype)
(None, 784)
<dtype: 'float32'>
>>> keras.utils.plot_model(model, "my_first_model.png")
>>> keras.utils.plot_model(model, "my_first_model_with_shape_info.png", show_shapes = True)

1.Training, Evaluation, Inference

使用 API:

  • .compile()

    • keras.losses.SparseCategoricalCrossentropy()
    • keras.optimizer.RMSprop()
  • .fit()

  • .evaluate()

步骤:

  • 编译
  • 训练
  • 评估
# mnist data
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255

# model compile
model.compile(
   loss = keras.SparseCategoricalCrossentropy(from_logits = True),
   optimizer = keras.optimizer.RMSprop(),
   metrics = ["accuracy"],
)

# model train
history = model.fit(x_train, y_train, batch_size = 64, epochs = 2, validation_split = 0.2)

# model evaluate
test_scores = model.evaluate(x_test, y_test, verbose = 2)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])

2.Save, Serialize

使用 API:

  • .save()
  • keras.model.load_model()

保存内容:

  • model architecture
  • model weight values
  • model train config
  • optimizer and its state(as passed to compile)
  • to restart training where left off
model.save("path_to_my_model")
del model
# Recreate the exact same model purely from the file
model = keras.models.load_model("path_to_my_model")

3.模型网络层共享

3.1 网络层共享

# Encoder
encoder_input = keras.Input(shape = (28, 28, 1), name = "img")
x = layers.Conv2D(16, 3, activation = "relu")(encoder_input)
x = layers.Conv2D(32, 3, activation = "relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation = "relu")(x)
x = layers.Conv2D(16, 3, activation = "relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
encoder = keras.Model(encoder_input, encoder_output, name = "encoder")
encoder.summary()

# decoder
x = layers.Reshape((4, 4, 1))(encoder_output)
x = layers.Conv2DTranspose(16, 3, activation = "relu")(x)
x = layers.Conv2DTranspose(32, 3, activation = "relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation = "relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation = "relu")(x)

# AutoEncoder
autoencoder = keras.Model(encoder_input, decoder_output, name = "autoencoder")
autoencoder.summary()

3.2 模型共享

a model is just like a layer.

示例 1: AutoEncoder

# Encoder
encoder_input = keras.Input(shape = (28, 28, 1), name = "original_img")
x = layers.Conv2D(16, 3, activation = "relu")(encoder_input)
x = layers.Conv2D(32, 3, activation = "relu")(x)
x = layers.MaxPooling2D(3)(x)
x = layers.Conv2D(32, 3, activation = "relu")(x)
x = layers.Conv2D(16, 3, activation = "relu")(x)
encoder_output = layers.GlobalMaxPooling2D()(x)
encoder = keras.Model(encoder_input, encoder_output, name = "encoder")
encoder.summary()

# decoder
decoder_input = keras.Input(shape = (16,), name = "encoded_img")
x = layers.Reshape((4, 4, 1))(decoder_input)
x = layers.Conv2DTranspose(16, 3, activation = "relu")(x)
x = layers.Conv2DTranspose(32, 3, activation = "relu")(x)
x = layers.UpSampling2D(3)(x)
x = layers.Conv2DTranspose(16, 3, activation = "relu")(x)
decoder_output = layers.Conv2DTranspose(1, 3, activation = "relu")(x)
decoder = keras.Model(decoder_input, decoder_output, name = "decoder")
decoder.summary()

# AutoEncoder
autoencoder_input = keras.Input(shape = (28, 28, 1), name = "img")
encoded_img = encoder(autoencoder_input)
decoded_img = decoder(encoded_img)
autoencoder = keras.Model(autoencoder_input, decoded_img, name = "autoencoder")
autoencoder.summary()

示例 2: Ensembling

def get_model():
   inputs = keras.Input(shape = (128,))
   outputs = keras.Dense(1)(inputs)

   return keras.Model(inputs, outputs)

model1 = get_model()
model2 = get_model()
model3 = get_model()

inputs = keras.Input(shape = (128,))
y1 = model1(inputs)
y2 = model2(inputs)
y3 = model3(inputs)
outputs = layers.average([y1, y2, y3])

ensemble_model = keras.Model(inputs = inputs, outputs = outputs)

3.3 复杂拓扑图模型

  • 模型有多个输入、输出
  • ResNet

示例 1:


4.自定义层扩展 API

5.Sequential API、Functional API、Model subclassing API 混搭

units = 32
timesteps = 10
input_dim = 4

# Define a Functional model
inputs = keras.Input(shape = (None, units))
x = layers.GloabalAveragePooling1D()(inputs)
outputs = layers.Dense(1)()
model = keras.Model(inputs, outputs)

# Define a subclassing model
class CustomRNN(layres.Layer):

   def __init__(self):
      super(CustomRNN, self).__init__()
      self.units = units
      self.projection_1 = layers.Dense(units = units, activation = "tanh")
      self.projection_2 = layers.Dense(units = units, activation = "tanh")
      # Our previously-defined Functional model
      self.classifier = model

   def call(self, inputs):
      outputs = []
      state = tf.zeros(shape = (inputs.shape[0], self.units))
      for t in range(inputs.shape[1]):
         x = inputs[:, t, :]
         h = self.projection_1(x)
         y = h + self.projection_2(state)
         state = y
         outputs.append(y)
      features = tf.stack(outputs, axis = 1)
      print(features.shape)

      return self.classifier(features)
run_model = CustomRNN()
_ = run_model(tf.zeros((1, timesteps, input_dim)))

Keras Subclasssing

import tensorflow as tf
from tensorflow as keras

1.Layer class

2.add_loss method

3.add_metric method

4.call method

5.Model class

6.The Functional API

Keras 网络层

1.Keras Layers 共有的方法:

from keras import layers
  • layer.get_weights()
  • layer.set_weights(weights)
  • layer.get_config()
    • keras.layer.Dense.from_config(config)
    • keras.layer.deserialize({“class_name”: , “config”: config})
  • 如果 Layer 是单个节点(不是共享 layer),可以使用以下方式获取 layer 的属性:
    • layer.input
    • layer.output
    • layer.input_shape
    • layer.output_shape
  • 如果 Layer 具有多个节点(共享 layer),可以使用以下方式获取 layer 的属性:
    • layer.getinputat(note_index)
    • layer.getoutputat(note_index)
    • layer.getinputshapeat(noteindex)
    • layer.getoutputshaepat(noteindex)

2.Keras Layers

  • Core Layers
    • Dense
    • Activation
    • Drop
    • Flatten
    • Input
    • Reshape
      • keras.layers.Reshape(target_shape)
    • Permute
    • RepeatVector
    • Lambda
    • ActivityRegularization
    • Masking
    • SpatialDropout1D
    • SpatialDropout2D
    • SpatialDropout3D
  • Convolutional Layers
    • 卷积层
      • Conv1D
      • Conv2D
      • Conv3D
      • SeparableConv1D
        • keras.layers.SeparableConv1D(rate)
      • SeparableConv2D
      • DepthwiseConv3D
    • Transpose
      • Conv2DTranspose
      • Conv3DTranspose
    • Cropping
      • Cropping1D
      • Cropping2D
      • Cropping3D
    • UnSampling
      • UnSampling1D
      • UnSampling2D
      • UnSampling3D
    • ZeroPadding
      • ZeroPadding1D
      • ZeroPadding2D
      • ZeroPadding3D
  • Pooling Layers
    • 最大池化
      • MaxPolling1D()
      • MaxPolling2D()
      • MaxPolling3D()
      • GlobalMaxPolling1D()
      • GlobalMaxPolling2D()
      • GlobalMaxPolling3D()
    • 平均池化
      • AveragePolling1D()
      • AveragePolling2D()
      • AveragePolling3D()
      • GlobalAveragePolling1D()
      • GlobalAveragePolling2D()
      • GlobalAveragePolling3D()
  • Locally-connected Layers
    • LocallyConnected1D()
    • LocallyConnected2D()
  • Recurrent Layers
    • RNN
      • RNN()
      • SimpleRNN()
      • SimpleRNNCell()
    • GRU
      • GRU()
      • GRUCell()
    • LSTM
      • LSTM()
      • LSTMCell()
      • ConvLSTM2D()
      • ConvLSTM2DCell()
    • CuDNN
      • CuDNNGRU()
      • CuDNNLSTM()
  • Embedding Layers
    • Embedding()
  • Merge Layers
    • Add()
    • Subtract()
    • Multiply()
    • Average()
    • Maximum()
    • Minimum()
    • Concatenate()
    • Dot()
    • add()
    • subtract()
    • multiply()
    • average()
    • maximum()
    • minimum()
    • concatenate()
    • dot()
  • Advanced Activations Layers
    • LeakyReLU()
    • PReLU()
    • ELU()
    • ThresholdedReLU()
    • Softmax()
    • ReLU()
    • Activation Functions
  • Normalization Layers
    • BatchNormalization()
  • Nosise Layers
    • GaussianNoise()
    • GaussianDropout()
    • AlphaDropout()
  • Others
    • Layer wrapper
      • TimeDistributed()
      • Bidirectional()
    • Writting Customilize Keras Layers
      • build(input_shape)
      • call(x)
      • compute_output_shape(input_shape)

3.Keras Layers 配置

model.add(Layer(
    # 输出、输出
    output_dim,
    input_dim,
    # 参数初始化
    kernel_initializer,
    bias_initializer,
    # 参数正则化
    kernel_regularizer,
    activity_regularizer,
    # 参数约束
    kernel_constraint,
    bias_constraint,
    # 层激活函数
    activation,
))
# 输出
input_s = Input()
# 激活函数
model.add(Activation)

3.1 Activation Function

  • Keras Activations
    • Activation layer
    • activation argument supported by all forward layers

调用方法:

from keras.layers import Activation, Dense
from keras import backend as K

# method 1
model.add(Dense(64))
model.add(Activation("tanh"))

# method 2
model.add(Dense(64, activation = "tanh"))

# method 3
model.add(Dense(64, activation = K.tanh))

3.2 可用的 activations

  • softmax: Softmax activation function
    • x =>
    • keras.activatons.softmax(x, axis = 1)
  • relu: Rectified Linear Unit
    • x => max(x, 0)
    • keras.activations.relu(x, alpha = 0.0, max_value = None, threshold = 0.0)
  • tanh: Hyperbolic tangent activation function
    • keras.activations.tanh(x)
  • sigmoid: Sigmoid activation function
    • x => 1/(1 + exp(-x))
    • keras.activations.sigmoid(x)
  • linear: Linear activation function
    • x => x
    • keras.activations.linear(x)

3.2 Keras 参数初始化(Initializers)

Initializers 的使用方法:

初始化定义了设置 Keras Layer 权重随机初始的方法
  • kernel_initializer param
    • “random_uniform”
  • bias_initializer param

可用的 Initializers:

  • keras.initializers.Initializer()
    • 基类
  • keras.initializers.Zeros()
    • 0
  • keras.initializers.Ones()
    • 1
  • keras.initializers.Constant()
    • keras.initializers.Constant(value = 0)
      • 0
    • keras.initializers.Constant(value = 1)
      • 1
  • keras.initializers.RandomNormal(mean = 0.0, stddev = 0.05, seed = None)
    • 正态分布
  • keras.initializers.RandomUniform(minval = 0.05, maxval = 0.05, seed = None)
    • 均匀分布
  • keras.initializers.TruncatedNormal(mean = 0.0, stddev = 0.05, seed = None)
    • 截尾正态分布:生成的随机值与 RandomNormal 生成的类似,但是在距离平均值两个标准差之外的随机值将被丢弃并重新生成。这是用来生成神经网络权重和滤波器的推荐初始化器
  • keras.initializers.VarianveScaling(scale = 1.0, mode = “fan_in”, distribution = “normal”, seed = None)
    • 根据权值的尺寸调整其规模
  • keras.initializers.Orthogonal(gain = 1.0, seed = None)
  • keras.initializers.Identity(gain = 1.0)
    • 生成单位矩阵的初始化器。仅用于 2D 方阵
  • keras.initializers.lecun_normal()
    • LeCun 正态分布初始化器
    • 它从以 0 为中心,标准差为 stddev = sqrt(1 / fanin) 的截断正态分布中抽取样本, 其中 fanin 是权值张量中的输入单位的数量
  • keras.initializers.lecun_uniform()
    • LeCun 均匀初始化器
    • 它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(3 / fanin), fanin 是权值张量中的输入单位的数量
  • keras.initializers.glorot_normal()
    • Glorot 正态分布初始化器,也称为 Xavier 正态分布初始化器
    • 它从以 0 为中心,标准差为 stddev = sqrt(2 / (fan*in + fan*out)) 的截断正态分布中抽取样本, 其中 fanin 是权值张量中的输入单位的数量, fanout 是权值张量中的输出单位的数量
  • keras.initializers.glorot_uniform()
    • Glorot 均匀分布初始化器,也称为 Xavier 均匀分布初始化器
    • 它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / (fan*in + fan*out)), fanin 是权值张量中的输入单位的数量, fanout 是权值张量中的输出单位的数量
  • keras.initializers.he_normal()
    • He 正态分布初始化器
    • 它从以 0 为中心,标准差为 stddev = sqrt(2 / fanin) 的截断正态分布中抽取样本, 其中 fanin 是权值张量中的输入单位的数量
  • keras.initializers.he_uniform()
    • He 均匀分布方差缩放初始化器
    • 它从 \([-limit,limit]\) 中的均匀分布中抽取样本, 其中 \(limit\)\(sqrt(6 / fan_in)\), 其中 fan_in 是权值张量中的输入单位的数量
  • 自定义 Initializer
from keras import backend as K

def my_init(shape, dtype = None):
    return K.random_normal(shape, dtype = dtype)

model.add(Dense(64, kernel_initializer = my_init))

3.3 Keras 正则化(Regularizers)

正则化器允许在优化过程中对层的参数层的激活函数情况进行惩罚,并且神经网络优化的损失函数的惩罚项也可以使用

惩罚是以层为对象进行的。具体的 API 因层而异,但 Dense,Conv1D,Conv2D 和 Conv3D 这些层具有统一的 API

Regularizers 的使用方法:

  • [class] keras.regularizers.Regularizer
    • [instance] kernel_regularizer param
    • [instance] bias_regularizer param
    • [instance] activity_regularizer param

可用的 Regularizers:

  • keras.regularizers.l1(0.)
  • keras.regularizers.l2(0.)
  • keras.regularizers.l1_l2(l1 = 0.01, l2 = 0.01)
  • 自定义的 Regularizer:
    • def l1_reg: pass

3.4 Keras 约束(Constraints)

constraints 模块的函数允许在优化期间对网络参数设置约束(例如非负性)。

约束是以层为对象进行的。具体的 API 因层而异,但 Dense,Conv1D,Conv2D 和 Conv3D 这些层具有统一的 API

Constraints 的使用方法:

  • kernel_constraint
  • bias_constraint

可用的 Constraints:

  • keras.constraints.MaxNorm(max_value = 2, axis = 0)
    • 最大范数权值约束
  • keras.constraints.NonNeg()
    • 权重非负的约束
  • keras.constraints.UnitNorm()
    • 映射到每个隐藏单元的权值的约束,使其具有单位范数
  • keras.constraints.MinMaxNorm(minvalue = 0, maxvalue = 1.0, rate = 1.0, axis = 0)
    • 最小/最大范数权值约束:映射到每个隐藏单元的权值的约束,使其范数在上下界之间

Keras 模型

  • Sequential model
  • Model class used with the function API

1.Keras 模型共有的方法和属性

from keras.model import Model
from keras.model import model_from_json, model_from_yaml
  • model.layers
  • model.inputs
  • model.outputs
  • model.summary()
  • Config
    • model.get_config()
      • Model.from_config()
      • Sequential.from_config()
  • Weights
    • model.get_weights()
      • to Numpy arrays
    • model.set_weights(weights)
      • from Numpy arrays
    • model.save_weights(filepath)
      • to HDF5 file
    • model.loadweights(filepath, byname = False)
      • from HDF5 file
  • Save or Load
    • model.to_json()
      • modelfromjson()
    • modeltoyaml()
      • modelfromyaml()

2.Model subclassing

  • 构建 full-customizable model by subclassing the Model class
  • 实现 forward pass in the call method
  • 模型的 layers 定义在 __init__(self, ...)
  • 模型的前向传播定义在 call(self, inputs)
  • 可以通过调用制定的自定义损失函数 self.add_loss(loss_tensor)
  • 在 subclassing 模型中,模型的拓扑结构被定义为 Python 代码,而不是 layers 的静态图,因此无法检查或序列化模型的拓扑结构,即以下方法不适用于 subclassing 模型:
    • model.inputs
    • model.outputs
    • model.to_yaml()
    • model.to_json()
    • model.get_config()
    • model.save()
  • 模型(keras.model.Model)子类的 API 可以为实现更加复杂的模型提供了灵活性,但是是有代价的,除了以上的功能不能使用,并且模型更复杂,更容易出错

示例:

import keras

class SimpleMLP(keras.Model):

    def __init__(self, use_bn = False, use_dp = False, num_classes = 10):
        super(SimpleMLP, self).__init__(name = "mlp")
        self.use_bn = use_bn
        self.use_dp = use_dp
        self.num_classes = num_classes

        # layers
        self.dense1 = keras.layers.Dense(32, activation = "relu")
        self.dense2 = keras.layers.Dense(num_classes, activation = "softmax")
        if self.use_dp:
            self.dp = keras.layers.Dropout(0.5)
        if self.use_bn:
            self.bn = keras.layers.BatchNormalization(axis = -1)

    def call(self, inputs):
        """
        前向传播
        """
        x = self.dense1(inputs)
        if self.use_dp:
            x = self.dp(x)
        if self.use_bn:
            x = self.bn(x)

        return self.dense2(x)

model = SimpleMLP()
model.compile(...)
model.fit(...)

3.Keras Sequential 模型的使用文档

Sequential 模型是层(layers)的线性堆叠

3.1 Keras Sequential Hello World

# in Python
from keras.model import Sequential
from keras.layers import Dense, Activation

# ==========
# 模型构建
# ==========
model = Sequential()
model.add(Dense(units = 64, activation = "relu", input_dim = 784))
model.add(Dense(units = 64, activation = "relu"))
model.add(Dense(units = 10, activation = "softmax"))

# model = Sequential([
#     Dense(64, input_shape = (784,)),
#     Activation("relu"),
#     Dense(64),
#     Activation("relu")
#     Dense(10),
#     Activation("softmax")
# ])

# ==========
# 模型编译
# ==========
# model.compile(loss = "categorical_crossentropy",
#               optimizer = "sgd",
#               metrics = ["accuracy"])

model.compile(loss = keras.losses.categorical_crossentropy
              optimizer = keras.optimizer.SGD(lr = 0.01, momentum = 0.9, nesterov = True),
              metrics = keras.metircs.Accuracy)

# ==========
# 模型训练
# ==========
model.fit(x_train, y_train, epochs = 5, batch_size = 32)
# model.train_on_batch(x_batch, y_batch)

# ==========
# 模型评估
# ==========
loss_and_metrics = model.evaluate(x_test, y_test, batch_size = 128)

# ==========
# 模型预测
# ==========
classes = model.predict(x_test, batch_size = 128)

4.Keras 函数式API 的使用文档

  • Keras 函数式 API 是定义复杂模型的方法
  • Keras 函数式 API 可以重用经过训练的模型,可以通过在张量上调用任何模型并将其视为一个层(layers)
    • 调用模型的结构
    • 调用模型的权重

4.1 Keras 函数式API Hello World

A densely-connected network:

from keras.layers import Input, Dense
from keras.models import Model

# ==========
# 模型构建
# ==========
inputs = Input(shape = (784,))
x = Dense(64, activation = "relu")(inputs)
x = Dense(64, activation = "relu")(x)
predictions = Dense(10, activation = "softmax")(x)
model = Model(inputs = inputs, outputs = predictions)

# ==========
# 模型编译
# ==========
model.compile(optimizer = "rmsprop",
              loss = "categorical_crossentropy",
              metrics = ["accuracy"])

# ==========
# 模型训练
# ==========
model.fit(data, labels)

4.2 函数式 API 特点

  • 所有模型都像层(layer)一样可以调用
  • 多输入和多输出模型
  • 共享图层
  • “层节点”概念

所有模型都像层(layer)一样可以调用:

# 将图像分类模型转换为视屏分类模型
from keras.layers import TimeDistributed
from keras.layers import Input, Dense
from keras.models import Model

inputs = Input(shape = (784,))
x = Dense(64, activation = "relu")(inputs)
x = Dense(64, activation = "relu")(x)
predictions = Dense(10, activation = "softmax")(x)
model = Model(inputs = inputs, outputs = predictions)

input_sequences = Input(shape = (20, 784))
processed_sequences = TimeDistributed(model)(input_sequences)

多输入和多输出模型:

from keras.layers import Input, Embedding, LSTM, Dense
from keras.models import Model

# ==========
# 模型构建
# ==========
# 标题输入
main_input = Input(shape = (100,), dtype = "int32", name = "main_input")
x = Embedding(output_dim = 512, input_dim = 10000, input_length = 100)(main_input)
lstm_out = LSTM(32)(x)
auxiliary_output = Dense(1, activation = "sigmoid", name = "aux_output")(lstm_out)

# 标题发布时间等数据输入
auxiliary_input = Input(shape = (5,), name = "aux_input")

# concatenate the lstm_out and auxiliary_input
x = keras.layers.concatenate([lstm_out, auxiliary_input])
x = Dense(64, activation = "relu")(x)
x = Dense(64, activation = "relu")(x)
x = Dense(64, activation = "relu")(x)
main_output = Dense(1, activation = "sigmoid", name = "main_output")(x)
model = Model(inputs = [main_input, auxiliary_input],
              outputs = [main_output, auxiliary_output])

# ==========
# 模型编译
# ==========
model.compile(optimizer = "rmsprop",
              loss = {
                "main_output": "binary_crossentropy",
                "aux_output": "binary_crossentropy"
              },
              loss_weights = {
                "main_output": 1,
                "aux_output": 0.2
              })

# ==========
# 模型训练
# ==========
model.fit(
    {
        "main_input": headline_data,
        "aux_input": additional_data
    },
    {
        "main_output": labels,
        "aux_output": labels
    },
    epochs = 50,
    batch_size = 32
)

共享图层:

任务:判断两条推文是否来自同一个人

import keras
from keras.layers import Input, LSTM, Dense
from keras.models import Model

# ==========
# 数据处理
# ==========


# ==========
# 模型构建
# ==========
# input layers
tweet_a = Input(shape = (280, 256))
tweet_b = Input(shape = (280, 256))
# LSTM layers
shared_lstm = LSTM(64)
encoded_a = shared_lstm(tweet_a)
encoded_b = shared_lstm(tweet_b)
# concatenate two vectors
merged_vector = keras.layers.concatenate([encoded_a, encoded_b], axis = -1)
# output layers(add a logistic regression on top)
predictions = Dense(1, activation = "sigmoid")(merged_vector)
model = Model(inputs = [tweet_a, tweet_b],
              output = predictions)

# ==========
# 模型编译
# ==========
model.compile(optimizer = "rmsprop",
              loss = "binary_crossentropy",
              metrics = ["accuracy"])

# ==========
# 模型训练
# ==========
model.fit([data_a, data_b], epochs = 10)

“层节点”概念:

输出层连接到单个输入层:

from keras.layers import Input, LSTM

a = Input(shape = (280, 256))

lstm = LSTM(32)
encoded_a = lstm(a)

# assert lstm.output == encoded_a

output = lstm.output
output_shape = lstm.output_shape

输出层连接到多个输入层:

from keras.layers import Input, LSTM
a = Input(shape = (280, 256))
b = Input(shape = (280, 256))

lstm = LSTM(32)

encoded_a = lstm(a)
encoded_b = lstm(b)

# lstm.output
# lstm.output_shape
# assert lstm.get_output_at(0) == encoded_a
# assert lstm.get_output_at(1) == encoded_b

output0 = lstm.get_output_at(0)
output1 = lstm.get_output_at(1)
output0_shape = lstm.get_output_shape_at(0)
output1_shape = lstm.get_output_shape_at(1)

4.3 函数式 API 模型例子

Inception module

论文

from keras.layers import Input, Conv2D, MaxPooling2D
from keras.models import Model

input_img = Input(shape = (256, 256, 3))
tower_1 = Conv2D(64, (1, 1), padding = "same", activation = "relu")(input_img)
tower_1 = Conv2D(64, (3, 3), padding = "same", activation = "relu")(tower_1)

tower_2 = Conv2D(64, (1, 1), padding = "same", activation = "relu")(input_img)
tower_2 = Conv2D(64, (5, 5), padding = "same", activation = "relu")(tower_2)

tower_3 = MaxPooling2D((3, 3), strides = (1, 1), padding = "same")(input_img)
tower_3 = Conv2D(64, (1, 1), padding = "same", activation = "relu")(tower_3)

output = keras.layers.concatenate([tower_1, tower_2, tower_3], axis = 1)

model = Modle(input = "",
              output = "")

model.compile()

model.fit()

Residual connection on a convolution layer

论文

from keras.layers import Input, Conv2D
from keras.model import Model

x = Input(shape = (256, 256, 3))
y = Conv2D(3, (3, 3), padding = "same")(x)
output = keras.layers.add([x, y])

model = Model(input = "",
              output = "")

model.compile()

model.fit()

Shared vision model Visual question answering model Video question answering model

5.回调函数-Callbacks

  • 回调函数是一个函数的集合,会在训练的阶段使用
  • 可以使用回调函数查看训练模型的内在状态和统计。也可以传递一个列表的回调函数(作为 callbacks关键字参数)到 SequentialModel 类型的 .fit() 方法。在训练时,相应的回调函数的方法会被在各自的阶段被调用

回调函数:

  • keras.callbacks.Callback()
    • 用来创建新的回调函数的抽象基类
    • .params
    • .model
  • keras.callbacks.BaseLogger(stateful_metrics = None)
    • 基类训练 epoch 评估值的均值
  • keras.callbacks.TerminateOnNaN()
    • 当遇到损失为 NaN 停止训练
  • keras.callbacks.ProgbarLogger()
  • keras.callbacks.History()
    • 所有事件都记录到 History 对象
  • keras.callbacks.ModelCheckpoint()
    • 在每个训练期之后保存模型
  • keras.callbacks.EarlyStopping()
  • keras.callbacks.RemoteMonitor()
  • keras.callbacks.LearningRateScheduler(schedule, verbose = 0)
  • keras.callbacks.TensorBoard()
  • keras.callbacks.ReduceLROnPlateau()
  • keras.callbacks.CSVLogger()
  • keras.callbacks.LambdaCallback()

创建回调函数:

from keras.layers import Dense, Activation
from keras.models import Sequential
from keras.callbacks import ModelCheckpoint

# 模型建立
model = Sequenital()
model.add(Dense(10, input_dim = 784, kernel_initializer = "uniform"))
model.add(Activation("softmax"))

# 模型编译
model.compile(loss = "categorical_crossentropy",
              optimizer = "rmsporp")

# 模型训练
# 在训练时,保存批量损失值
class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs = {}):
        self.losses = []

    def on_batch_end(self, batch, logs = {}):
        self.losses.append(logs.get("loss"))
history = LossHistory()

# 如果验证集损失下降,在每个训练 epoch 后保存模型
checkpointer = ModelCheckpoint(filepath = "/tmp/weight.hdf5",
                               verbose = 1,
                               save_best_only = True)
model.fit(x_train, y_train,
          batch_size = 128, epochs = 20,
          verbose = 0,
          validation_data = (x_test, y_test),
          callbacks = [history, checkpointer])

# 模型结果输出
print(history.losses)

6.Applications

Keras Applications(keras.applications) 提供了预训练好的深度学习模型,这些模型可以用于预测、特征提取等.

当初始化一个模型时就会自动下载,默认下载的路径是:~/.keras.models/.

可用的模型:

在 ImageNet 数据上预训练过的用于图像分类的模型
  • Xception
  • VGG16
  • VGG19
  • ResNet, ResNetV2, ResNeXt
  • InceptionV3
  • InceptionResNet2
  • MobileNet
  • MobileNetV2
  • DenseNet
  • NASNet
from keras.applications.xception import Xception
from keras.applications.vgg16 import VGG16
from keras.applications.vgg19 import VGG19
from keras.applications.resnet50 import ResNet50
from keras.applications.inception_v3 import InceptionV3
from keras.applications.inception_resnet_v2 import InceptionResNetV2
from keras.applications.mobilenet import MobileNet
from keras.applications.densenet import DenseNet121
from keras.applications.densenet import DenseNet169
from keras.applications.densenet import DenseNet201
from keras.applications.nasnet import NASNetLarge
from keras.applications.nasnet import NASNetMobile
from keras.applications.mobilenet_v2 import MobileNetV2

# channels_last only; 299x299
xception_model = Xception(include_top = True,
                          weights = "imagenet",
                          input_tensor = None,
                          input_shape = None,
                          pooling = None,
                          classes = 1000)
# channels_first and channels_last; 224x224
vgg16_model = VGG16(include_top = True,
                    weights = "imagenet",
                    input_tensor = None,
                    input_shape = None,
                    pooling = None,
                    classes = 1000)
vgg19_model = VGG19(include_top = True,
                    weights = 'imagenet',
                    input_tensor = None,
                    input_shape = None,
                    pooling = None,
                    classes = 1000)
resnet50_model = ResNet50(include_top = True,
                          weights = 'imagenet',
                          input_tensor = None,
                          input_shape = None,
                          pooling = None,
                          classes = 1000)
inception_v3_model = InceptionV3(include_top = True,
                                 weights = 'imagenet',
                                 input_tensor = None,
                                 input_shape = None,
                                 pooling = None,
                                 classes = 1000)
inception_resnet_v2_model = InceptionResNetV2(include_top = True,
                                              weights = 'imagenet',
                                              input_tensor = None,
                                              input_shape = None,
                                              pooling = None,
                                              classes = 1000)
mobilenet_model = MobileNet(input_shape = None,
                            alpha = 1.0,
                            depth_multiplier = 1,
                            dropout = 1e-3,
                            include_top = True,
                            weights = 'imagenet',
                            input_tensor = None,
                            pooling = None,
                            classes = 1000)
densenet_model = DenseNet121(include_top = True,
                             weights = 'imagenet',
                             input_tensor = None,
                             input_shape = None,
                             pooling = None,
                             classes = 1000)
densenet_model = DenseNet169(include_top = True,
                             weights = 'imagenet',
                             input_tensor = None,
                             input_shape = None,
                             pooling = None,
                             classes = 1000)
densenet_model = DenseNet201(include_top = True,
                             weights = 'imagenet',
                             input_tensor = None,
                             input_shape = None,
                             pooling = None,
                             classes = 1000)
nasnet_model = NASNetLarge(input_shape = None,
                           include_top = True,
                           weights = 'imagenet',
                           input_tensor = None,
                           pooling = None,
                           classes = 1000)
nasnet_model = NASNetMobile(input_shape = None,
                            include_top = True,
                            weights = 'imagenet',
                            input_tensor = None,
                            pooling = None,
                            classes = 1000)
mobilenet_v2_model = MobileNetV2(input_shape = None,
                                 alpha = 1.0,
                                 depth_multiplier = 1,
                                 include_top = True,
                                 weights = 'imagenet',
                                 input_tensor = None,
                                 pooling = None,
                                 classes = 1000)

图像分类模型使用示例:

from keras.preprocessing import image
from keras.applications.resnet50 import ResNet50
from keras.applications.resnet50 import preprocess_input, decode_prediction
import numpy as np

# Load model
model = ResNet50(weights = "imagenet")

# Image data
img_path = "elephant.jpg"
img = image.load_img(img_path, target_size = (224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis = 0)
x = preprocess_input(x)

preds = model.predict(x)
print("Predicted:", decode_prediction(preds, top = 3)[0])

Keras Callbacks

1.常用损失函数

2.损失函数的使用

Keras Utilities

1.模型可视化(Model plotting utilities)

1.1 plot_model()

  • Converts a Keras model to dot format and save to a file.
import tensorflow as tf

tf.keras.utils.plot_model(
    model,
    to_file = "model.png",
    show_shapes = False,
    show_dtype = False,
    show_layer_names = True,
    rankdir = "TB",
    expand_nested = False,
    dpi = 96,
)

1.2 model_to_dot()

  • Convert a Keras model to dot format.
import tensorflow as tf

tf.keras.utils.model_to_dot(
    model,
    show_shapes = False,
    show_dtype = False,
    show_layer_names = True,
    rankdir = "TB",             # "TB": a vertical plot; "LR": a horizontal plot
    expand_nested = False,
    dpi = 96,
    subgraph = False,
)

2.序列化工具(Serialization utilities)

  • custom_object_scope()
  • get_custom_objects()
  • register_keras_serializable()
  • serialize_keras_object()
  • daserialize_keras_object()

2.1 CustomObjectScope class

  • 作用

    • 将自定义类/函数 暴露给 Keras 反序列化内部组件
    • 在范围 with custom_object_scope(object_dict),Keras 方法将能够反序列化已保存的配置引用的任何自定义对象
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.custom_object_scope(*args)
    
  • 示例

    # 一个自定义的正则化器 `my_regularizer`
    my_regularizer = None
    
    # a layer
    layer = Dense(3, kernel_regularizer = my_regularizer)
    
    # Config contains a reference to "my_regularizer"
    config = layer.get_config()
    ...
    
    # Later
    with custom_object_scope({"my_regularizer": my_regularizer}):
        layer = Dense.from_config(config)
    

2.2 get_custom_objects()

  • 作用

    • 额,下次一定
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.get_custom_objects()
    
  • 示例

    get_custom_objects().clear()
    get_custom_objects()["MyObject"] = MyObject
    

2.3 register_keras_serializable()

  • 作用

    • 额,下次一定
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.register_keras.serializable(package = "Custom", name = None)
    

2.4 serialize_keras_object()

  • 作用

    • 将 Keras 对象序列化为 Json 兼容的表示形式
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.serialize_keras_object(instance)
    

2.5 daserialize_keras_object()

  • 作用

    • 将 Keras 对象的序列化形式转换回实际对象
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.deserialize_keras_object(
        identifier,
        module_objects = None,
        custom_objects = None,
        printable_module_name = "object"
    )
    

3.Python & Numpy utilities

3.1 to_categorical()

  • 作用

    • 将一个类别型向量(整数)转换为 二元类别矩阵
    • 类似于 one-hot
  • 语法

    import tensorflow as tf
    
    utils.to_categorical(y,
                         num_classes = None,
                         dtypes = "float32")
    
  • 示例

    # example 1
    a = tf.keras.utils.to_categorical([0, 1, 2, 3], num_classes = 4)
    a = tf.constant(a, shape = [4, 4])
    print(a)
    
    # example 2
    b = tf.constant([.9, .04, .03, .03,
                     .3, .45, .15, .13,
                     .04, .01, .94, .05,
                     .12, .21, .5, .17],
                     shape = [4, 4])
    loss = tf.keras.backend.categorical_crossentropy(a, b)
    print(np.around(loss, 5))
    
    # example 3
    loss = tf.keras.backend.categorical_crossentropy(a, a)
    print(np.around(loss, 5))
    

3.2 normalize()

  • 作用

    • 标准化一个 Numpy 数组
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.normalize(x, axis = -1, order = 2)
    

3.3 get_file()

  • 作用

    • Downloads a file from a URL if it not already in the cache.
    • By default the file at the url origin is downloaded to the cache_dir ~/.keras, placed in the cache_subdir datasets, and given the filename fname. The final location of a file example.txt would therefore be ~/.keras/datasets/example.txt.
    • Files in tar, tar.gz, tar.bz, and zip formats can also be extracted. Passing a hash will verify the file after download. The command line programs shasum and sha256sum can compute the hash.
  • 语法

    tf.keras.utils.get_file(
        fname,
        origin,
        untar=False,
        md5_hash=None,
        file_hash=None,
        cache_subdir="datasets",
        hash_algorithm="auto",
        extract=False,
        archive_format="auto",
        cache_dir=None,
    )
    
  • 示例

    import tensorflow
    
    path_to_downloaded_file = tf.keras.utils.get_file(
        "flower_photos",
        "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz",
        untar = True
    )
    

3.4 Progbar class

  • 作用

    • 显示进度条
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.Progbar(
        target,
        width = 30,
        verbose = 1,
        interval = 0.05,
        stateful_metrics = None,
        unit_name = "step"
    )
    

3.5 Sequence class

  • 作用

    • 用于拟合数据序列(如数据集)的基础对象
    • 每个人都Sequence必须实现__getitem__和__len__方法。如果您想在各个时期之间修改数据集,则可以实现 on_epoch_end。该方法__getitem__应返回完整的批次
    • Sequence是进行多处理的更安全方法。这种结构保证了网络在每个时期的每个样本上只会训练一次,而生成器则不会
  • 语法

    import tensorflow as tf
    
    tf.keras.utils.Sequence()
    
  • 示例

    from skimage.io import imread
    from skimage.transform import resize
    import numpy as np
    import math
    
    # Here, `x_set` is list of path to the images
    # and `y_set` are the associated classes.
    
    class CIFAR10Sequence(Sequence):
    
        def __init__(self, x_set, y_set, batch_size):
            self.x, self.y = x_set, y_set
            self.batch_size = batch_size
    
        def __len__(self):
            return math.ceil(len(self.x) / self.batch_size)
    
        def __getitem__(self, idx):
            batch_x = self.x[idx * self.batch_size:(idx + 1) *
            self.batch_size]
            batch_y = self.y[idx * self.batch_size:(idx + 1) *
            self.batch_size]
    
            return np.array([
                resize(imread(file_name), (200, 200))
                for file_name in batch_x]), np.array(batch_y)
    

4.Backend utilities

  • clear_session()
  • floatx()
  • set_floatx()
  • image_data_format()
  • set_image_data_format()
  • epsilon()
  • set_epsilon()
  • is_keras_tensor()
  • get_uid()
  • rnn()

4.1 clear_session()

4.1 floatx()

  • 作用

    • 返回默认的 float 类型
  • 语法

    import tensorflow as tf
    
    tf.keras.backend.floatx()
    

4.1 set_floatx()

  • 作用

    • 设置 float 类型
  • 语法

    import tensorflow as tf
    
    tf.keras.backend.set_floatx()
    
  • 示例

    import tensorflow as tf
    
    tf.keras.backend.floatx()
    tf.keras.backend.set_floatx("float64")
    tf.keras.backend.floatx()
    tf.keras.backend.set_floatx("float32")
    

4.1 image_data_format()

  • 作用

    • 返回设置图像数据格式约定的值
  • 语法

    import tensorflow as tf
    
    tf.keras.backend.image_data_format(data_format)
    

4.1 set_image_data_format()

  • 作用

    • 设置图像数据格式约定的值
  • 语法

    import tensorflow as tf
    
    tf.keras.backend.set_image_data_format(data_format)
    
  • 示例

    tf.keras.backend.image_data_format()
    
    tf.keras.backend.set_image_data_format("channels_first")
    tf.keras.backend.set_image_data_format("channels_last")
    

4.1 epsilon()

  • 作用

    • 返回数字表达式中使用的模糊因子的值
  • 语法

    tf.keras.backend.epsilon()
    

4.1 set_epsilon()

  • 作用

    • 设置数字表达式中使用的模糊因子的值
  • 语法

    tf.keras.backend.set_epsilon()
    

4.1 is_keras_tensor()

4.1 get_uid()

4.1 rnn()

Keras 模型编译

model.compile(loss,
              optimizer,
              metrics)

1. Losses

  • Loss Function
  • Objective Function
  • Optimization score Function

(1) Keras loss functions

回归:

from keras import losses

# 回归
from keras.losses import mean_squared_error
from keras.losses import mean_absolute_error
from keras.losses import mean_absolute_percentage_error
from keras.losses import mean_squared_logarithmic_error
from keras.losses import squared_hinge
from keras.losses import hinge
from keras.losses import categorical_hinge
from keras.losses import logcosh
model.Compile(loss = ["mse", "MSE", mean_squared_error],
                       optimizer,
                       metircs)
model.Compile(loss = ["mae", "MAE", mean_absolute_error],
                       optimizer,
                       metircs)
model.Compile(loss = ["mape", "MAPE", mean_absolute_percentage_error],
                       optimizer,
                       metircs)
model.Compile(loss = ["msle", "MLSE", mean_squared_logarithmic_error],
                       optimizer,
                       metircs)

分类:

# 分类
from keras.losses import categorical_crossentropy
from keras.losses import sparse_categorical_crossentropy
from keras.losses import binary_crossentropy
from keras.losses import kullback_leibler_divergence
from keras.losses import poisson
from keras.losses import cosine_proximity
model.Compile(loss = ["kld", "KLD", kullback_leibler_divergence],
                       optimizer,
                       metircs)
model.Compile(loss = ["cosine", cosine_proximity],
                       optimizer,
                       metircs)

2. Metrics

  • Metric 是一个评估模型表现的函数
  • Metric 函数类似于一个损失函数,只不过模型评估返回的 metric 不用来训练模型,因此,可以使用任何损失函数当做一个 metric 函数使用

2.1 Keras metrics

Keras API:

from keras import metrics
from keras.metrics import binary_accuracy
from keras.metrics import categorical_accuracy
from keras.metrics import sparse_categorical_accuracy
from keras.metrics import top_k_categorical_accuracy
from keras.metrics import sparse_top_k_categorical_accuracy
from keras.metrics import mae

from keras.losses import mean_squared_error
from keras.losses import mean_absolute_error
from keras.losses import mean_absolute_percentage_error
from keras.losses import mean_squared_logarithmic_error
from keras.losses import squared_hinge
from keras.losses import hinge
from keras.losses import categorical_hinge
from keras.losses import logcosh
from keras.losses import categorical_crossentropy
from keras.losses import sparse_categorical_crossentropy
from keras.losses import binary_crossentropy
from keras.losses import kullback_leibler_divergence
from keras.losses import poisson
from keras.losses import cosine_proximity

Metrics Name:

metrics = ["acc", "accuracy"]

2.3 自定义 metrics

import keras.backend as K

def mean_pred(y_true, y_pred):
     return K.mean(y_pred)

model.compile(optimizers = "rmsprop",
                       loss = "binary_accuracy",
                       metrics = ["accuracy", mean_pred])

3. Optimizers

3.1 Keras optimizder 的使用方式

  1. keras.optimizersoptimizer 参数
from keras import optimizers

# 编译模型
sgd = optimizers.SGD(lr = 0.01, decay = 1e-6, momentum = 0.9, nesterov = True)
model.compile(loss = "mean_squared_error",
              optimizer = sgd)
  1. optimizer 参数
# 编译模型
model.compile(loss = "mean_squared_error",
              optimizer = "sgd")

3.2 Keras optimizers 的共有参数

  • control gradient clipping
    • clipnorm
    • clipvalue
from keras import optimizers

# All parameter gradients will be clipped to
# a maximum norm of 1.
sgd = optimizers.SGD(lr = 0.01, clipnorm = 1)

# All parameter gradients will be clipped to
# a maximum value of 0.5 and
# a minimum value of -0.5.
sgd = optimizers.SGD(lr = 0.01, clipvalue = 0.5)

3.3 Keras Optimizers

  • SGD
  • RMSprop
  • Adagrad
  • Adadelta
  • Adam
  • Adamax
  • Nadam
from keras import optimizers

sgd = optimizers.SGD(lr = 0.01)
model.compile(loss, optimizer = sgd)
# or
model.compile(loss, optimizer = "sgd")

rmsprop = optimizers.RMSprop(lr = 0.001)
model.compile(loss, optimizer = rmsprop)
# or
model.compile(loss, optimizer = "rmsprop")

adagrad = optimizers.Adagrad(lr = 0.01)
model.compile(loss, optimizer = adagrad)
# or
model.compile(loss, optimizer = "adagrad")

adadelta = optimizers.Adadelta(lr = 1.0)
model.compile(loss, optimizer = adadelta)
# or
model.compile(loss, optimizer = "adadelta")

adam = optimizers.Adam(lr = 0.001)
model.compile(loss, optimizer = adam)
# or
model.compile(loss, optimizer = "adam")

adamax = optimizers.Adamax(lr = 0.02)
model.compile(loss, optimizer = adamax)
# or
model.compile(loss, optimizer = "adamax")

nadam = optimizers.Nadam(lr = 0.002)
model.compile(loss, optimizer = nadam)
# or
model.compile(loss, optimizer = "nadam")

Keras 训练

test

test

Keras 损失函数

The purpose of loss functions is to compute the quantity that a model should seek to minimize during training.

1.常用损失函数

  • class handle

    • 可以传递配置参数
  • function handle

1.1 概率损失(Probabilistic losses)

  • BinaryCrossentropy class

    • binary_crossentropy() function
  • CategoricalCrossentropy class

    • categorical_crossentropy() function
  • SparseCategoricalCrossentropy class

    • sparse_categorical_crossentropy() function
  • Possion class

    • possion() function
  • KLDivergence class

    • kl_divergence() function
1.1.1 class & function() 使用方法
  • 作用

    • 二分类损失函数

      • BinaryCrossentropy & binary_crossentropy
      • Computes the cross-entropy loss between true labels and predicted labels.
    • 二分类、多分类

      • CategoricalCrossentropy & categorical_crossentropy
      • SparseCategoricalCrossentropy & sparse_categorical_crossentropy
    • 其他

  • 语法

    tf.keras.losses.Class(
        from_loits = False,
        label_smoothing = 0,
        reduction = "auto",
        name = ""
    )
    
  • 示例

    # data
    y_ture = [[0., 1.], [0., 0.]]
    y_pred = [[0.6, 0.4], [0.4, 0.6]]
    
    # reduction="auto" or "sum_over_batch_size"
    bce = tf.keras.losses.BinaryCrossentropy()
    bce(y_true, y_pred).numpy()
    
    # reduction=sample_weight
    bce = tf.keras.losses.BinaryCrossentropy()
    bce(y_true, y_pred, sample_weight = [1, 0]).numpy()
    
    # reduction=sum
    bce = tf.keras.losses.BinaryCrossentropy(reduction = tf.keras.losses.Reduction.SUM)
    bce(y_true, y_pred).numpy()
    
    # reduction=none
    bce = tf.keras.losses.BinaryCrossentropy(reduction = tf.keras.losses.Reduction.NONE)
    bce(y_true, y_pred).numpy()
    

1.2 回归损失(Regression losses)

  • MeanSquaredError class

    • mean_squared_error function
  • MeanAbsoluteError class

    • mean_absolute_error function
  • MeanAbsolutePercentageError class

    • mean_absolute_percentage_error function
  • MeanSquaredLogarithmicError class

    • mean_squared_logarithmic_error function
  • CosineSimilarity class

    • cosine_similarity function
  • Huber class

    • huber function
  • LogCosh class

    • log_cosh function

1.3 Hinge losses for “maximum-margin” classification

  • Hinge class

    • hinge function
  • SquaredHinge class

    • squared_hinge function
  • CategoricalHinge class

    • categorical_hinge function

2.损失函数的使用——compile() & fit()

  • 通过实例化一个损失类创建损失函数,可以传递配置参数

    from tensorflow import keras
    from tensorflow.keras import layers
    
    model = keras.Sequential()
    model.add(layers.Dense(64, kernel_initializer = "uniform", input_shape = (10,)))
    model.add(layers.Activation("softmax"))
    
    model.compile(
        loss = keras.losses.SparseCategoricalCrossentropy(from_logits = True),
        optimizer = "adam",
        metrics = ["acc"]
    )
    
  • 直接使用损失函数

    from tensorflow.keras.losses import sparse_categorical_crossentropy
    
    model.compile(
        loss = "sparse_categorical_crossentropy",
        optimizer = "adam",
        metrics = ["acc"]
    )
    

3.损失函数的使用——单独使用

tf.keras.losses.mean_squared_error(tf.ones((2, 2)), tf.zeros((2, 2)))
loss_fn = tf.keras.losses.MeanSquaredError(resuction = "sum_over_batch_size")
loss_fn(tf.ones((2, 2)), tf.zeros((2, 2)))

loss_fn = tf.keras.losses.MeanSquaredError(reduction = "sum")
loss_fn(tf.ones((2, 2)), tf.zeros((2, 2)))

loss_fn = tf.keras.losses.MeanSquaredError(reduction = "none")
loss_fn(tf.ones((2, 2)), tf.zeros((2, 2)))

loss_fn = tf.keras.losses.mean_squared_error
loss_fn(tf.ones((2, 2,)), tf.zeros((2, 2)))

loss_fn = tf.keras.losses.MeanSquaredError()
loss_fn(tf.ones((2, 2)), tf.zeros((2, 2)))

4.创建自定义损失函数

  • Any callable with the signature loss_fn(y_true, y_pred) that returns an array of losses (one of sample in the input batch) can be passed to compile() as a loss.
  • Note that sample weighting is automatically supported for any such loss.

示例:

def my_loss_fn(y_true, y_pred):
    squared_difference = tf.square(y_true - y_pred)
    return tf.reduce_mean(squared_difference, axis = -1)

model.compile(optimizer = "adam", loss = my_loss_fn)

5. add_loss() API

from tensorflow.keras.layers import Layer

class MyActivityRegularizer(Layer):
    """Layer that creates an activity sparsity regularization loss."""

    def __init__(self, rate = 1e-2):
        super(MyActivityRegularizer, self).__init__()
        self.rate = rate

    def call(self, inputs):
        self.add_loss(self.rate * tf.reduce_sum(tf.square(inputs)))

        return inputs

from tensorflow.keras import layers

class SparseMLP(Layer):
    """Stack of Linear layers with a sparsity regularization loss."""

    def __init__(self, output_dim):
        super(SparseMLP, self).__init__()
        self.dense_1 = layers.Dense(32, activation=tf.nn.relu)
        self.regularization = MyActivityRegularizer(1e-2)
        self.dense_2 = layers.Dense(output_dim)

    def call(self, inputs):
        x = self.dense_1(inputs)
        x = self.regularization(x)
        return self.dense_2(x)

mlp = SparseMLP(1)
y = mlp(tf.ones((10, 10)))

print(mlp.losses)  # List containing one float32 scalar

mlp = SparseMLP(1)
mlp(tf.ones((10, 10)))
assert len(mlp.losses) == 1
mlp(tf.ones((10, 10)))
assert len(mlp.losses) == 1  # No accumulation.

Keras 评价指标

1.常用评价指标

1.1 Accuracy metrics

  • Accuracy class
  • BinaryAccuracy class
  • CategoricalAccuracy class
  • TopKCategoricalAccuracy class
  • SparseTopKCategoricalAccuracy class

1.2 Probabilistic metrics

  • BinaryCrossentropy class
  • CategoricalCrossentropy class
  • SparseCategoricalCrossentropy class
  • KLDivergence class
  • Poisson class

1.3 Regression metrics

  • MeanSquaredError class
  • RootMeanSquaredError class
  • MeanAbsoluteError class
  • MeanAbsolutePercentageError class
  • CosineSimilarity class
  • LogCoshError class

1.4 Classification metrics based on True/False positives & negatives

  • AUC class
  • Precision class
  • Recall class
  • TurePositives class
  • TrueNegatives class
  • FalsePositives class
  • FalseNegatives class
  • PrecisionAtRecall class
  • SensitivityAtSpecificity class
  • SpecificityAtSensitivity class

1.5 image segmentation metrics

  • MeanIoU class

1.6 Hinge metrics for “maximum-margin” Classification

  • Hinge class
  • SquaredHinge class
  • CategoricalHinge class

2.评价指标的使用——compile() & fit()

3.评价指标的使用——单独使用

4.创建自定义评价指标

5. add_metric() API

Keras 优化器

1.常用 Keras 优化器

  • SGD
  • RMSprop
  • Adam
  • Adadelta
  • Adagrad
  • Adamax
  • Nadam
  • Ftrl

1.1 TODO

2.Keras 优化器的使用

2.1 模型编译(compile)和拟合(fit)

from tensorflow import keras
from tensorflow.keras import layers

# model
model = keras.Sequential()
model.add(layers.Dense(64, kernel_initializer = "uniform", input_shape = (10,)))
model.add(layers.Activate("softmax"))
# model compile
opt = keras.optimizers.Adam(learning_rate = 0.01)
model.compile(loss = "categorical_crossentropy", optimizer = opt)
# model.compile(loss = "categorical_crossentropy", optimizer = "adam")

2.2 自定义迭代训练

# Instantiate an optimizer
optimizer = tf.keras.optimizer.Adam()

# Iterate over the batches of a dataset.
for x, y in dataset:
    # open a GradientTape
    with tf.GradientTape() as tape:

        # Forward pass.
        logits = model(x)

        # Loss value for this batch
        loss_value = loss_fn(y, logits)

    # Get gradients of loss wrt the weights
    gradients = tape.gradient(loss_value, model.trainable_weights)

    # Update the weights of the model
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

2.3 学习率衰减(decay)、调度(sheduling)

  • 可以使用学习率时间表来调整优化器的学习率如何随时间变化

    • ExponentialDecay: 指数衰减
    • PiecewiseConstantDecay:
    • PolynomialDecay: 多项式衰减
    • InverseTimeDecay: 逆时间衰减
lr_schedule = keras.optimizers.schedules.ExponentialDecay(
    initial_learning_rate = 1e-2,
    decay_steps = 10000,
    decay_rate = 0.9
)
optimizer = keras.optimizers.SGD(learning_rate = lr_schedule)

3.Keras 优化算法核心 API

  • apply_gradients
  • weights_property
  • get_weights
  • set_weights

3.1 apply_gradients

  • 语法

    Optimizer.apply_gradients(
        grads_and_vars, name=None, experimental_aggregate_gradients=True
    )
    
  • 参数

    • grads_and_vars: 梯度、变量对的列表
    • name: 返回的操作的名称
    • experimental_aggregate_gradients:
  • 示例

    grads = tape.gradient(loss, vars)
    grads = tf.distribute.get_replica_context().all_reduce("sum", grads)
    
    # Processing aggregated gradients.
    optimizer.apply_gradients(zip(grad, vars), experimental_aggregate_gradients = False)
    

3.2 weights_property

  • 语法

    import tensorflow as tf
    
    tf.keras.optimizers.Optimizer.weights
    

3.3 get_weights

  • 语法

    Optimizer.get_weights()
    
  • 示例

    # 模型优化器
    opt = tf.keras.optimizers.RMSprop()
    
    # 模型构建、编译
    m = tf.keras.models.Sequential()
    m.add(tf.keras.layers.Dense(10))
    m.compile(opt, loss = "mse")
    
    # 数据
    data = np.arange(100).reshape(5, 20)
    labels = np.zeros(5)
    
    # 模型训练
    print("Training")
    results = m.fit(data, labels)
    print(opt.get_weights)
    

3.4 set_weights

  • 语法

    Optimizer.set_weights(weights)
    
  • 示例

    # 模型优化器
    opt = tf.keras.optimizers.RMSprop()
    
    # 模型构建、编译
    m = tf.keras.models.Sequential([tf.keras.layers.Dense(10)])
    m.compile(opt, loss = "mse")
    
    # 数据
    data = np.arange(100).reshape(5, 20)
    labels = np.zeros(5)
    
    # 模型训练
    print("Training")
    results = m.fit(data, labels)
    
    # 优化器新权重
    new_weights = [
        np.array(10),       # 优化器的迭代次数
        np.ones([20, 10]),  # 优化器的状态变量
        np.zeros([10])      # 优化器的状态变量
    ]
    opt.set_weights(new_weights)
    opt.iteration
    

Keras Applications

1.Keras 介绍

Keras Applications are deep learning models that are made available alongside pre-trained weights. These models can be used for prediction, feature extraction, and fine-tuning.

Weights are downloaded automatically when instantiating a model. They are stored at ~/.keras/models/.

Upon instantiation, the models will be built according to the image data format set in your Keras configuration file at ~/.keras/keras.json. For instance, if you have set image_data_format=channels_last, then any model loaded from this repository will get built according to the TensorFlow data format convention, “Height-Width-Depth”.

  • ~/.keras/models/
  • ~/.keras/keras.json

2.目前可用的 App

  • Xception
  • EfficientNet B0 to B7
  • VGG16 and VGG19
  • ResNet and ResNetV2
  • MobileNet and MobileNetV2
  • DenseNet
  • NasNetLarge and NasNetMobile
  • InceptionV3
  • InceptionResNetV2

Python Deeplearning

  • Keras Sequential
    • 模型假设,网路只有一个输入和一个输出,而且网络是层的线性堆叠;
  • 有些网络需要多个独立的输入,有些网络则需要多个输出,而有些网络在层与层之间具有内部分支,这样的网络看起来像是层构成的图(graph),而不是层的线性堆叠;
    • 多模态(multimodal)输入
    • 元数据
    • 文本描述
    • 图片
    • 预测输入数据的多个目标属性
    • 类别
    • 连续值
    • 非线性地网络拓扑结构,网络结构是有向无环图
    • Inception 系列网络
      • 输入被多个并行的卷积分支所处理,然后将这些分支的输出合并为单个张量;
    • ResNet 系列网络
      • 向模型中添加残差连接(residual connection),将前面的输出张量与后面的输出张量相加, 从而将前面的表示重新注入下游数据流中,这有助于防止信息处理流程中的信息损失;

1.多输入模型

  • Keras 函数式 API
    • 可以构建具有多个输入的模型,通常情况下,这种模型会在某一时刻用一个可以组合多个张量的层将不同输入分支合并,张量组合方式可能是相加,连接等,比如:
  • keras.layers.add
  • keras.layers.concatenate
  • 问答模型:
  • 输入:
    • 自然语言描述的问题
    • 文本片段,提供用于回答问题的信息
  • 输出
    • 一个回答,在最简单的情况下,这个回答只包含一个词,可以通过对某个预定义的词表做softmax得到;
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import *

from tensorflow.keras.model import Model
# from keras.model import Model
from tensorflow.keras import layers, Input
# from keras import layers, Input

# =========================================================================
# 构建模型
# =========================================================================
text_vocabulary_size = 10000
question_vocabulary_size = 10000
answer_vocabulary_size = 500
# 文本片段
text_input = Input(
   shape = (None,),
   dtype = "int32",
   name = "text"
)
embedded_text = layers.Embedding(text_vocabulary_size, 64)(text_input)
encoded_text = layres.LSTM(32)(embedded_text)
# 自然语言描述的问题
question_input = Input(
   shape = (None,),
   dtype = "int32",
   name = "question"
)
embedded_question = layers.Embedding(question_vocabulary_size, 32)(question_input)
encoded_question = layers.LSTM(16)(embedded_question)

concatenated = layers.concatenate([encoded_text, encoded_question], axis = -1)

answer = layers.Dense(answer_vocabulary_size, activation = "softmax")(concatenated)

model = Model(inputs = [text_input, question_input], outputs = answer)

model.compile(
   optimizer = "rmsprop",
   loss = "categorical_crossentropy",
   metrics = ["acc"]
)

# =========================================================================
# 训练模型
# =========================================================================
import numpy as np
num_samples = 1000
max_length = 100
text = np.random.randint(1, text_vocabulary_size, size = (num_samples, max_length))
question = np.random.randint(1, question_vocabulary_size, size = (num_samples, max_length))
answers = np.random.randint(answer_vocabulary_size, size = (num_samples))

answers = keras.utils.to_categorical(answers, answer_vocabulary_size)

model.fit([text, question], answers, epochs = 10, batch_size = 128)
model.fit(
   {
      "text": text,
      "question": question,
   },
   answers,
   epochs = 10,
   batch_size = 128
)

2.多输出模型

网络同时预测数据的不同性质
from keras import layers, Input
from keras.models import Model

vocabulary_size = 50000
num_income_groups = 10

# 输入层
posts_input = Input(shape = (None,), dtype = "int32", name = "posts")
embedded_posts = layers.Embedding(256, vocabulary_size)(posts_input)
# 隐藏层
x = layers.Conv1D(128, 5, activation = "relu")(embedded_posts)
x = layers.MaxPooling1D(5)(x)
x = layers.Conv1D(256, 5, activation = "relu")(x)
x = layers.Conv1D(256, 5, activation = "relu")(x)
x = layers.MaxPooling1D(5)(x)
x = layers.Conv1D(256, 5, activation = "relu")(x)
x = layers.Conv1D(256, 5, activation = "relu")(x)
x = layers.GlobalMaxPooling1D()(x)
x = layers.Dense(128, activation = "relu")(x)
# 输出层
age_prediction = layers.Dense(1, name = "age")(x)
income_prediction = layers.Dense(num_income_groups, activation = "softmax", name = "income")(x)
gender_prediction = layers.Dense(1, activation = "sigmoid", name = "gender")(x)
# 构建模型
model = Model(posts_input, [age_prediction, income_prediction, gender_prediction])

model.compile(optimizer = "rmsprop", loss = ["mse", "categorical_crossentropy", "binary_crossentropy"])
model.compile(
   optimizer = "rmsprop",
   loss = {
      "age": "mse",
      "income": "categorical_crossentropy",
      "gender": "binary_crossentropy"
   }
)

3.经验总结

3.1 机器、深度学习任务问题

  • 二分类
  • 多分类
  • 标量回归

3.2 回归问题

  • 回归问题使用的损失函数

    • 均方误差(MSE)
  • 回归问题使用的评估指标

    • 平均绝对误差(MAE)
  • 回归问题网络的最后一层只有一个单元,没有激活,是一个线性层,这是回归的典型设置,添加激活函数会限制输出范围

3.3 二分类问题

  • 二分类问题使用的损失函数

    • 对于二分类问题的 sigmoid 标量输出,binary_crossentropy
  • 对于二分类问题,网络的最后一层应该是只有一个单元并使用 sigmoid 激活的 Dense 层,网络输出应该是 0~1 范围内的标量,表示概率值

3.4 数据预处理问题

  • 在将原始数据输入神经网络之前,通常需要对其进行预处理

    • 结构化数据
    • 图像数据
    • 文本数据
  • 将取值范围差异很大的数据输入到神经网络中是有问题的

    • 网路可能会自动适应这种取值范围不同的数据,但学习肯定变得更加困难
    • 对于这种数据,普遍采用的最佳实践是对每个特征做标准化,即对于输入数据的每个特征(输入数据矩阵中的列), 减去特征平均值,再除以标准差,这样得到的特征平均值为 0,标准差为 1
    • 用于测试数据标准化的均值和标准差都是在训练数据上计算得到的。在工作流程中,不能使用测试数据上计算得到的任何结果, 即使是像数据标准化这么简单的事情也不行
  • 如果输入数据的特征具有不同的取值范围,应该首先进行预处理,对每个特征单独进行缩放

3.5 样本量问题

  • 如果可用的数据很少,使用 K 折交叉验证可以可靠地评估模型
  • 如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型模型,以避免严重的过拟合
    • 较小的网络可以降低过拟合

3.6 网络结构选择问题

  • 如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型模型,以避免严重的过拟合
  • 如果数据被分为多个类别,那么中间层过小可能会导致信息瓶颈

3.7 优化器

  • 无论你的问题是什么,rmsprop 优化器通常都是足够好的选择

PyTorch Cheat Sheet

1.Imports

# General
import torch # root package
from torch.utils.data import Dataset, Dataloader # dataset representation and loading

# Neural Network API
from torch import Tensor
import torch.nn as nn
import torch.nn.functional as F
import torch.autograd as autograd
import torch.optim as optim
from torch.jit import script, trace

# Torchscript and JIT
torch.jit.trace()
@script

# ONNX
torch.onnx.export(model, dummy data, xxxx.proto)
model = onnx.load("alexnet.proto)
onnx.checker.check_model(model)
onnx.helper.printable_graph(model.graph)

# Vision
from torchvision import datasets, models, transforms
import torchvision.transforms as transforms

# Distributed Training
import torch.distributed as dist
from torch.multiprocessing import Process

2.Tensors

import torch

# Creation
x = torch.randn(*size)
x = torch.[ones|zeros](*size)
x = torch.tensor(L)
y = x.clone()
with torch.no_grad():
requires_grad = True

# Dimensionality
x.size()
x = torch.cat(tensor_seq, dim = 0)
y = x.view(a, b, ...)
y - x.view(-1,a)
y = x.transpose(a, b)
y = x.permute(*dims)
y = x.unsqueeze(dim)
y = x.unsqueeze(dim = 2)
y = x.squeeze()
y = x.squeeze(dim = 1)

# Algebra
ret = A.mm(B)
ret = A.mv(x)
x = x.t()

# GUP Usage
torch.cuda.is_available
x = x.cuda()
x = x.cpu()
if not args.disable_cuda and torch.cuda.is_available():
   args.device = torch.device("cuda")
else:
   args.device = torch.device("cpu")
net.to(device)
x = x.to(device)

3.Deep Learning

import torch.nn as nn

nn.Linear(m, n)
nn.ConvXd(m, n, s)
nn.MaxPoolXd(s)
nn.BatchNormXd
nn.RNN
nn.LSTM
nn.GRU
nn.Dropout(p = 0.5, inplace = False)
nn.Dropout2d(p = 0.5, inplace = False)
nn.Embedding(num_embeddings, embedding_dim)

# Loss Function
nn.X

# Activation Function
nn.X

# Optimizers
opt = optim.x(model.parameters(), ...)
opt.step()
optim.X

# Learning rate scheduling
scheduler = optim.X(optimizer, ...)
scheduler.step()
optim.lr_scheduler.X

4.Data Utilities

# Dataset
Dataset
TensorDataset
Concat Dataset

# Dataloaders and DataSamplers
DataLoader(dataset, batch_size = 1, ...)
sampler.Sampler(dataset, ...)
sampler.XSampler where ...

pytorch tensor

内容概要

  • 1.tensor 的简介及创建

    • tensor 是多维数组

    • tensor 的创建

      • 直接创建

        • torch.tensor
        • torch.from_numpy
      • 依数值创建

        • empty
        • ones
        • zeros
        • eye
        • full
        • arange
        • linspace
      • 依概率分布创建

        • torch.normal
        • torch.randn
        • torch.rand
        • torch.randint
        • torch.randperm
  • 2.tensor 的操作

    • tensor 的基本操作

      • tensor 的拼接

        • torch.cat()
        • torch.stack()
      • tensor 的切分

        • torch.chunk()
        • torch.split()
      • tensor 的索引

        • index_select()
        • masked_select()
      • tensor 的变换

        • torch.reshape()
        • torch.transpose()
        • torch.t
    • tensor 的数学运算

      • add(input, aplha, other)

1.pytorch tensor 的创建

1.1 tensor 的介绍

tensor 是 pytorch 中最基本的概念,其参与了整个运算过程,这里主要介绍 tensor 的概念和属性,如 data, variable, device 等, 并且介绍 tensor 的基本创建方法,如直接创建、依属主创建、依概率分布创建等.

  • tensor

    • tensor 其实是多维数组,它是标量、向量、矩阵的高维拓展
  • tensor 与 variable

    • 在 pytorch 0.4.0 版本之后 variable 已经并入 tensor,但是 variable 这个数据类型对于理解 tensor 来说很有帮助, variable 是 torch.autograd 中的数据类型

    • variable(torch.autograd.variable) 有 5 个属性, 这些属性都是为了 tensor 的自动求导而设置的:

      • data
      • grad
      • grad_fn
      • requires_grad
      • is_leaf
    • tensor(torch.tensor) 有 8 个属性:

      • 与数据本身相关

        • data: 被包装的 tensor
        • dtype:tensor 的数据类型,如 torch.floattensor, torch.cuda.floattensor, float32, int64(torch.long)
        • shape: tensor 的形状
        • device:tensor 所在的设备, gpu/cup,tensor 放在 gpu 上才能使用加速
      • 与梯度求导相关

        • requires_grad: 是否需要梯度
        • grad: data 的梯度
        • grad_fn: fn 表示 function 的意思,记录创建 tensor 时用到的方法
        • is_leaf: 是否是叶子节点(tensor)

1.2 tensor 的创建

from __future__ import print_function
import numpy as np
import torch
1.2.1 直接创建

1.torch.tensor(): 从 data 创建 tensor api

  • API:

    torch.tensor(
       data,                   # list, numpy
       dtype = none,
       device = none,
       requires_grad = false,
       pin_memory = false      # 是否存于锁页内存
    )
    
  • 示例:

    arr = np.ones((3, 3))
    t = torch.tensor(arr, device = "cuda")
    print(t)
    
  1. 通过 numpy array 来创建 tensor api

    • 创建的 tensor 与原 ndarray 共享内存,当修改其中一个数据的时候,另一个也会被改动

    • API:

      torch.from_numpy(ndarray)
      
    • 示例:

      arr = np.array([[1, 2, 3], [4, 5, 6]])
      t = torch.from_numpy(arr)
      print(arr)
      arr[0, 0] = 0
      print(arr, t)
      t[1, 1] = 100
      print(arr, t)
      
1.2.2 依数值创建
  • api:

    torch.zeros(
       *size,
       out = none,             # 输出张量,就是把这个张量赋值给另一个张量,但这两个张量一样,指的是同一个内存地址
       dtype = none,
       layout = torch.strided, # 内存的布局形式
       device = none,
       requires_grad = false
    )
    
  • 示例:

out_t = torch.tensor([1])
t = torch.zeros((3, 3), out = out_t)
print(out_t, t)
print(id(out_t), id(t), id(t) == id(out_t))

2.pytorch tensor 的操作

  • torch.add(, out)
  • .add_()
  • .view()
  • add:

    x = torch.zeros(5, 3, dtype = torch.long)
    y = torch.rand(5, 3)
    
    # method 1
    print(x + y)
    
    # method 2
    print(torch.add(x, y))
    
    # method 3
    result = torch.empty(5, 3)
    torch.add(x, y, out = result)
    print(result)
    
    # method 4
    y.add_(x)
    print(y)
    
  • index:

    x = torch.zeros(5, 3, dtype = torch.long)
    print(x[:, 1])
    
  • resize:

    x = torch.randn(4, 4)
    y = x.view(16)
    z = x.view(-1, 8)
    print(x.size(), y.size(), z.size())
    
  • object trans:

    x = torch.randn(1)
    print(x)
    print(x.item()) # python number
    
  • torch tensor 2 numpy array:

    a = torch.ones(5)
    b = a.numpy()
    print(a)
    print(b)
    
    a.add_(1)
    print(a)
    print(b)
    
  • numpy array 2 torch tensor:

    import numpy as np
    a = np.ones(5)
    b = torch.from_numpy(a)
    np.add(a, 1, out = a)
    print(a)
    print(b)
    

3.pytorch cuda tensor

# let us run this cell only if cuda is available
# we will use ``torch.device`` objects to move tensors in and out of gpu
if torch.cuda.is_available():
   device = torch.device("cuda")          # a cuda device object
   y = torch.ones_like(x, device=device)  # directly create a tensor on gpu
   x = x.to(device)                       # or just use strings ``.to("cuda")``
   z = x + y
   print(z)
   print(z.to("cpu", torch.double))       # ``.to`` can also change dtype together!

PyTorch nn

A typical training procedure for a neural network is as follows:

  • Define the neural network that has some learnable parameters (or weights)
  • Iterate over a dataset of inputs
  • Process input through the network
  • Compute the loss (how far is the output from being correct)
  • Propagate gradients back into the network’s parameters
  • Update the weights of the network, typically using a simple update rule: weight = weight - learning_rate * gradient

定义神经网络

# nn
# autograd
# nn.Module
    # forward(input) => output

import torch
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 1 input image channel, 6 output channels, 3x3 square convolution kernel
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 6 * 6, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Max pooling over a (2, 2) window
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)

        return x

    def num_flat_features(self, x):
        size = x.size()[1:]
        num_features = 1
        for s in size:
            num_features *= s

        return num_features


net = Net()
print(net)
params = list(net.parameters)
print(len(params))
print(params[0].size()) # conv1's .weight
net.zero_grad()
out.backward(torch.randn(1, 10))

PyTorch 自动求导

autograd 包提供了对所有 Tensor 的自动微分操作
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
`autograd` package:
-------------------
跟踪 torch.Tensor 上所有的操作:torch.Tensor(requires_grad = True)
自动计算所有的梯度:.backward()
torch.Tensor 上的梯度:.grad
torch.Tensor 是否被跟踪:.requires_grad
停止跟踪 torch.Tensor 上的跟踪历史、未来的跟踪:.detach()

with torch.no_grad():
    pass

Function
.grad_fn
"""

import torch

# --------------------
# 创建 Tensor 时设置 requires_grad 跟踪前向计算
# --------------------
x = torch.ones(2, 2, requires_grad = True)
print("x:", x)

y = x + 2
print("y:", y)
print("y.grad_fn:", y.grad_fn)

z = y * y * 3
print("z:", z)
print("z.grad_fn", z.grad_fn)

out = z.mean()
print("out:", out)
print("out.grad_fn:", out.grad_fn)
out.backward()
print("x.grad:", x.grad)

# --------------------
# .requires_grad_() 能够改变一个已经存在的 Tensor 的 `requires_grad`
# --------------------
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print("a.requires_grad", a.requires_grad)
a.requires_grad_(True)
print("a.requires_grad:", a.requires_grad)
b = (a * a).sum()
print("b.grad_fn", b.grad_fn)



# 梯度
x = torch.randn(3, requires_grad = True)
y = x * 2
while y.data.norm() < 1000:
    y = y * 2
v = torch.tensor([0.1, 1.0, 0.0001], dtype = torch.float)
y.backward(v)
print(x.grad)

# .requires_grad
print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)

# .detach()
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all())

4.PyTorch 动态图、自动求导

  • 4.1 计算图

    • 描述运算的有向无环图

      • Tensor 的 is_leaf 属性
      • Tensor 的 grad_fn 属性
  • 4.2 PyTorch 动态图机制

    • 动态图与静态图
  • 4.3 PyTorch 自动求导机制

    • torch.autograd.backward() 方法自动求取梯度

    • torch.autograd.grad() 方法可以高阶求导

    • note

      • 梯度不自动清零
      • 依赖叶节点的节点, requires_grad 默认为 True
      • 叶节点不能执行原位操作

4.1 计算图

计算图是用来描述运算的有向无环图。主要有两个因素:节点、边。 其中节点表示数据,如向量、矩阵、张量;而边表示运算,如加减乘除、卷积等。

使用计算图的好处不仅是让计算看起来更加简洁,还有个更大的优势是让梯度求导也变得更加方便。

  • 示例:

    x = torch.tensor([2.], requires_grad = True)
    w = torch.tensor([1.], requires_grad = True)
    
    a = torch.add(w, x)
    b = torch.add(w, 1)
    
    y = torch.mul(a, b)
    
    y.backward()
    print(w.grad)
    

4.2 Pytorch 动态图机制

4.3 PyTorch 自动求导机制

  • package autograd
  • torch.Tensor
  • .requires_grad = True
  • .backward()
  • .grad
  • .detach()
  • with torch.no_grad(): pass
  • .grad_fn

PyTorch 自动求导机制使用的是 torch.autograd.backward 方法,功能就是自动求取梯度。

  • API:

    torch.autograd.backward(
       tensors,
       gard_tensors = None,
       retain_graph = None,
       create_graph = False
    )
    

PyTorch 数据读取与预处理

PyTorch 数据

PyTorch 数据读取

构造自定义的 Datasets, Dataloaders, Transforms

依赖库

from __future__ import print_function, division
import os
import torch
import pandas as pd
from skimage import io, transform
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transform, utils

# Ignore warnings
import warnings
warnings.filterwarnings("ignore")

plt.ion() # interactive mode

读取数据

landmarks_frame = pd.read_csv("../data/faces/face_landmarks.csv")
n = 65
img_name = landmarks_frame.iloc[n, 0]
landmarks = landmarks_frame.iloc[n, 1:].as_matrix()
landmarks = landmarks.astype("float").reshape(-1, 2)


print("Image name: {}".format(img_name))
print("Landmarks shape: {}".format(landmarks.shape))
print("First 4 Landmarks: {}".format(landmarks[:4]))
def show_landmarks(image, landmarks):
   """show image with landmarks"""
   plt.imshow(image)
   plt.scatter(landmarks[:, 0], landmarks[:, 1], s = 10, marker = ".", c = "r")
   plt.pause(0.001)

plt.figure()
show_landmarks(io.imread(os.path.join("../data/faces/", img_name)), landmarks)

Dataset class

class FaceLandmarksDataset(Dataset):
   """Face Landmarks dataset."""
   def __init__(self, csv_file, root_dir, transform = None):
      pass

PyTorch 数据并行

让模型跑在 GPU 上

import torch

# 让模型在 GPU 上运行
device = torch.device("cuda:0")
model.to(device)

# 将 tensor 复制到 GPU 上
my_tensor = torch.ones(2, 2, dtype = torch.double)
mytensor = my_tensor.to(device)

让模型跑在多个 GPU 上

  • PyTorch 默认使用单个 GPU 执行运算
model = nn.DataParallel(model)
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

# Parameters and DataLoaders
input_size = 5
output_size = 2

batch_size = 30
data_size = 100

# Device
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
class RandomDataset(Dataset):

     def __init__(self, size, length):
             self.len = length
             self.data = torch.randn(length, size)

     def __getitem__(self, index):
             return self.data[index]

     def __len__(self):
             return self.len

rand_loader = DataLoader(dataset = RandomDataset(input_size, data_size),
                                              batch_size = batch_size,
                                              shuffle = True)
class Model(nn.Module):

     def __init__(self, input_size, output_size):
             super(Model, self)__init__()
             self.fc = nn.Linear(input_size, output_size)

     def forward(self, input):
             output = self.fc(input)
             print("\tIn Model: input size", input.size(),
                       "output size", output.size())

             return output
model = Model(input_size, output_size)
if torch.cuda.device_count() > 1:
     print("Let's use", torch.cuda.device_count(), "GPUs!")
     model = nn.DataParallel(model)

model.to(device)
for data in rand_loader:
     input = data.to(device)
     output = model(input)
     print("Outside: input size", input.size(),
               "output_size", output.size())

PyTorch 损失函数

test 1

test 2

PyTorch 模型保存

1.PyTorch 保存模型

import torch

1.1 方法 1:保存和加载模型参数

# 保存模型
torch.save(the_model.state_dict(), PATH)

# 加载模型
the_model = TheModelClass(*args, **kwargs)
the_model.load_state_dict(torch.load(PATH))

1.2 方法 2:保存和加载整个模型

# 保存模型
torch.save(the_model, PATH)

# 加载模型
the_model = torch.load(PATH)

PyTorch 模型

PyTorch 模型的创建

PyTorch 模型容器

PyTorch 权重初始化

1.权重初始化方法

正确的权重初始化可以加速模型的收敛,不恰当的权重初始化导致输出层的输出过大或者过小,最终导致梯度爆炸或者消失, 使得模型无法训练

  • 使用与饱和激活函数 tanh 等的 Xavier 初始化方法
  • 非饱和激活函数 relu 等的 Kaiming 初始化方法

2.损失函数

算法工程师技能列表

1.岗位行业

  • 搜索推荐
  • 广告
  • 数据挖掘
  • 机器学习
  • 信息检索
  • 推荐系统

2.算法栈

  1. 数据结构和算法
  2. 图像分类
  3. 图像识别
  4. 目标检测
  5. 目标分类
  6. 目标跟踪
  7. 目标分割
  8. 通用 OCR
  9. 语义分割
  10. 实例分割
  11. 语音分析
  • 情感分析
  • 话题分析
  • 话题摘要
  • 观点识别
  • 语义理解
  1. 多模态情感计算

3.技术栈

  1. C++

  2. Java

  3. SQL

  4. NoSQL

  5. Python

    • Scikit-learn

    • Tensorflow

    • keras

    • pytorch

    • YOLOv5

    • deeplearning 部署框架

      • Tensorflow Lite
      • NCNN
      • MNN
      • TensorRT
      • Tengine
      • OpenVINO
      • NCNN
    • Python 单元测试框架

    • Python 文档框架

    • Python 包构建与分发经验

  6. CUDA编程

  7. OPENCL编程

  8. Linux

    • 嵌入式开发
  9. 神经网络

    • ResNet
    • EfficientNet
    • Yolo
    • Mask-RCNN
    • RetinaNet
    • DeepLab
  10. Spark

  11. Hadoop

  12. OpenCV

  13. PCL

  14. git

  15. adb

  16. shell

音频数据处理 Scipy

1.test

2.test 2

音频数据处理 librosa

1.test

2.test 2

NLP–opencc

  • 介绍

    • Open Chinese Convert(OpenCC,开放中文转换)是一个开源项目,用于在繁体中文,简体中文和日文汉字(Shinjitai)之间进行转换.

      • 支持中国大陆,台湾和香港之间的字符级和短语级转换,字符变体转换以及区域习语. 不是普通话和广东话等之间的翻译工具.
      • 支持词汇等级的转换,异体字转换和地区习惯用词转换(中国大陆,台湾,香港,日本新字体). 不提供普通话与粤语的转换.
  • 特点

    • 严格区分“一简对多繁”和“一简对多异”
    • 完全兼容异体字,可以实现动态替换
    • 严格审校一简对多繁词条,原则为“能分则不合”
    • 支持中国大陆,台湾,香港异体字和地区习惯用词转换,如「里」「里」,「鼠标」「滑鼠」
    • 词库和函数库完全分离,可以自由修改,引入,扩展
  • 在线演示 Demo(不支持API查询)

1.Python opencc 安装

# Windows, Linux, Mac
workon tf
pip install opencc

2.opencc 使用

2.1 基本配置文件

文件名 from => to 翻译
s2t.json Simplified Chinese to Traditional Chinese 简体到繁体
t2s.json Traditional Chinese to Simplified Chinese 繁体到简体
s2tw.json Simplified Chinese to Traditional Chinese(Taiwan Standard) 简体到台湾正体
tw2s.json Traditional Chinese (Taiwan Standard) to Simplified Chinese 台湾正体到简体
s2hk.json Simplified Chinese to Traditional Chinese(Hong Kong Standard) 简体到香港繁体(香港小学学习字词表标准)
hk2s.json Traditional Chinese (Hong Kong Standard) to Simplified Chinese 香港繁体(香港小学学习字词表标准)到简体
s2twp.json Simplified Chinese to Traditional Chinese(Taiwan Standard) with Taiwanese idiom 简体到繁体(台湾正体标准)并转换为台湾常用词汇
tw2sp.json Traditional Chinese (Taiwan Standard) to Simplified Chinese with Mainland Chinese idiom 繁体(台湾正体标准)到简体并转换为中国大陆常用词汇
t2tw.json Traditional Chinese (OpenCC Standard) to Taiwan Standard 繁体(OpenCC标准)到台湾正体
t2hk.json Traditional Chinese (OpenCC Standard) to Hong Kong Standard 繁体(OpenCC标准)到香港繁体(香港小学学习字词表标准)
t2jp.json Traditional Chinese Characters (Kyūjitai) to New Japanese Kanji(Shinjitai) 繁体(OpenCC标准,旧字体)到日文新字体
jp2t.json New Japanese Kanji (Shinjitai) to Traditional Chinese Characters(Kyūjitai) 日文新字体到繁体(OpenCC标准,旧字体)

2.2 Python API Demo

import opencc

# 简体中文 => 繁体中文
converter = opencc.OpenCC("t2s.json")
t_data = "漢字"
s_data = converter.convert(t_data)

2.3 命令行模式 Demo

opencc --help
opencc_dict --help
opencc_phrase_extract --help

NLP–Gensim

1.Gensim 简介

1.1 Gensim 简介

  • Gensim is a Free python library

  • Gensim is a topic modelling for humans

    • Train large-scale semantic NLP models
    • Represent text as semantic vectors
    • Find semantically related documents
  • Why Gensim?

    • super fast
    • data streaming
    • platform independent
    • proven
    • open source
    • ready-to-use models and corpora

1.2 Demo

from gensim import corpora, models, similarities, downloader

# Stream a training corpus directly from S3.
corpus = corpora.MmCorpus("s3://path/to/corpus")

# Train Latent Semantic Indexing with 200D vectors.
lsi = models.LsiModel(corpus, num_topics = 200)

# Convert another corpus to the LSI space and index it.
index = similarities.MatrixSimilarity(lsi[another_corpus])

# Compute similarity of a query vs indexed documents.
sims = index[query]

2.Gensim 安装

2.1 Gensim 安装

$ pip install --upgrade gensim

$ conda install -c conda-forge gensim

2.2 Gensim 依赖

  • Python 3.6, 3.7, 3.8
  • Numpy
  • smart_open: 用于打开远程存储库中的文件或压缩文件

3.Gensim 使用

import  pprint

3.1 Gensim 中常用核心概念

gensim 的核心概念:

  • Document
  • Corpus
  • Vector
  • Model
3.1.1 Document
document = "Human machine interface for lab abc computer applications"
3.1.2 Corpus

一个 Corpus 是一系列 Document 对象的集合,Corpora 在 Gensim 中提供了两个角色:

  • 1.``Model`` 训练的输入. 在训练期间,模型会使用该训练语料库来查找常见的主题和主题,从而初始化其内部模型参数

    • Gensim 专注于无监督模型,因此不需要人工干预,例如昂贵的注释或手工标记文档.
  • 2.整理好的 Document. 训练后,可以使用主题模型从新文档(训练语料库中未显示的文档)中提取主题

    • 可以为此类语料库索引 相似性查询,通过语义相似性查询,聚类等
from gensim import corpora, models, similarities, downloader
from collections import defaultdict

# 1.语料库
text_corpus = [
    "Human machine interface for lab abc computer applications",
    "A survey of user opinion of computer system response time",
    "The EPS user interface management system",
    "System and human system engineering testing of EPS",
    "Relation of user perceived response time to error measurement",
    "The generation of random binary unordered trees",
    "The intersection graph of paths in trees",
    "Graph minors IV Widths of trees and well quasi ordering",
    "Graph minors A survey",
]

# 2.创建常用词集合
# 停用词
stoplist = set("for a of the and to in".spilt(" "))
# 常用词
texts = [[word for word in document.lower().split() if word not in stoplist] for document in text_corpus]

# 3.统计词频
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1

# 4.删除只出现一次的词
processed_corpus = [[token for token in text if frequency[token] > 1] for text in texts]
pprint.pprint(processed_corpus)

# 5.将语料库中的每个单词与唯一的整数ID相关联
dictionary = corpora.Dictionary(processed_corpus)
print(dictionary)
3.1.3 Vector

为了推断语料库中的潜在结构,需要可以表示文档的数学处理方式:

  • 方法 1: 将文档表示为 特征向量

    • 密集向量
  • 方法 2: 词袋模型

    • 稀疏向量/词袋向量
pprint.pprint(dictionary.token2id)

使用 doc2bow 为文档创建单词袋表示法:

new_doc = "Human computer interaction"
new_vec = dictionary.doc2bow(new_doc.lower().split())
print(new_vec)
3.1.4 Model

常用模型:

  • tf-idf
from gensim import models

# 训练模型
tfidf = models.TfidfModel(bow_corpus)

# 转换 "system minors" 字符串
words = "system minors".lower().split()
print(tfidf[dictionary.doc2bow(words)])

3.2 语料(Corpora)和词空间(Vector Spaces)

3.3 主题(Topics)和转换(Transformations)

3.4 相似性查询(Similarity Queries)

3.5 常用教程

4.Gensim 常用 API

NLP–spaCy

1.spaCy 安装

  • 官网安装

    # Install spaCy
    $ pip install spacy
    $ pip install spacy-nightly --pre
    
    # Download model
    $ python -m spacy en
    
    # EN
    $ python -m spacy download en_core_web_lg
    $ python -m spacy download en_core_web_md
    $ python -m spacy download en_core_web_sm
    
    # ZH
    $ python -m spacy download en_core_web_lg
    $ python -m spacy download en_core_web_md
    $ python -m spacy download en_core_web_sm
    
    # Install textacy which will also be useful
    $ pip3 install -U textacy
    
  • pip 安装

    • (1)从 GitHub 上安装

      # EN
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      
      # ZH
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_md-1.2.0/en_core_web_md-1.2.0.tar.gz
      
    • (2)先从 Github 上下载安装包,再用 pip 或 python setup.py install 安装

      • GitHub 下载地址

      • 本地 pip 安装

        # EN
        $ pip install /your_path/en_core_web_lg-x.x.x.tar.gz
        $ pip install /your_path/en_core_web_md-x.x.x.tar.gz
        $ pip install /your_path/en_core_web_sm-x.x.x.tar.gz
        
        # ZH
        $ pip install /your_path/zh_core_web_lg-x.x.x.tar.gz
        $ pip install /your_path/zh_core_web_md-x.x.x.tar.gz
        $ pip install /your_path/zh_core_web_sm-x.x.x.tar.gz
        
      • 本地 python3 setup.py install 安装

        # (1)解压到 setup.py 路径
        # (2)安装
        # EN
        $ python3 setup.py install /your_path/en_core_web_lg-x.x.x.tar.gz
        $ python3 setup.py install /your_path/en_core_web_md-x.x.x.tar.gz
        $ python3 setup.py install /your_path/en_core_web_sm-x.x.x.tar.gz
        
        # ZH
        $ python3 setup.py install /your_path/zh_core_web_lg-x.x.x.tar.gz
        $ python3 setup.py install /your_path/zh_core_web_md-x.x.x.tar.gz
        $ python3 setup.py install /your_path/zh_core_web_sm-x.x.x.tar.gz
        

2.testing

NLP–fastText

1.fastText 简介

1.1 快速开始

1.1.1 fastText 是什么
fastText is a library for efficient learning of word representations and sentence classification.
1.1.2 fastText 环境依赖
  • 计算机系统

    • macOS
    • Linux
  • C++11 编译器

    • (gcc-4.6.3 or newer) or (clang-3.3 or newer)
    • make
  • Python 依赖

    • >=python 2.6
    • numpy
    • scipy
1.1.3 fastText 工具库构建

1.构建 fastText 为一个命令行工具(CLT)

$ git clone https://github.com/facebookresearch/fastText.git
$ cd fastText
$ make

或者:

$ wget https://github.com/facebookresearch/fastText/archive/v0.9.2.zip
$ unzip v0.9.2.zip
$ cd fastText-0.9.2
$ make

2.构建 fastText 为一个 Python 模块

$ git clone https://github.com/facebookresearch/fastText.git
$ cd fastText
$ sudo pip install .
# or
$ sudp python setup.py install

或者:

$ wget https://github.com/facebookresearch/fastText/archive/v0.9.2.zip
$ unzip v0.9.2.zip
$ cd fastText-0.9.2
$ pip install
>>> import fasttext
>>>

3.获取帮助:

./fasttext
>>> import fasttext
>>> help(fasttext.FastText)

2.使用 fastText 进行文本分类

文本分类可以应用在许多方面:

  • spam detection
  • sentiment analysis
  • smart replies

2.1 准备文本数据

  • 数据来源: https://cooking.stackexchange.com/

  • 数据描述:

    • building a classifier to automatically recognize the topic of a stackexchange question about cooking
  • 数据下载

    $ wget https://dl.fbaipublicfiles.com/fasttext/data/cooking.stackexchange.tar.gz
    $ tar xvzf cooking.stackexchange.tar.gz
    $ head cooking.stackexchange.txt
    $ wc cooking.stackexchange.txt
    
  • 数据格式预览

    Label document
    __label__sauce __label__cheese How much does potato starch affect a cheese sauce recipe?
    __label__food-safety __label__acidity Dangerous pathogens capable of growing in acidic environments
    __label__cast-iron __label__stove How do I cover up the white spots on my cast iron stove?
    __label__restaurant Michelin Three Star Restaurant; but if the chef is not there
    __label__knife-skills __label__dicing Without knife skills, how can I quickly and accurately dice vegetables?
    __label__storage-method __label__equipment __label__bread What’s the purpose of a bread box?
    __label__baking __label__food-safety __label__substitutions __label__peanuts how to seperate peanut oil from roasted peanuts at home?
    __label__chocolate American equivalent for British chocolate terms
    __label__baking __label__oven __label__convection Fan bake vs bake
    __label__sauce __label__storage-lifetime __label__acidity __label__mayonnaise Regulation and balancing of readymade packed mayonnaise and other sauces
  • 数据集分割

    • Training dataset

      $ head -n 12404 cooking.stackexchange.txt > cooking.train
      $ wc cooking.train
      
    • validation dataset

      $ tail -n 3000 cooking.stackexchange.txt > cooking.valid
      $ wc cooking.valid
      

2.2 构建分类器

  • 基本模型

    import fasttext
    
    # 模型训练
    model = fasttext.train_supervised(input = "cooking.train")
    
    # 模型保存
    model.save_model("model_cooking.bin")
    
    # 模型测试
    model.predict("Which baking dish is best to bake a banana bread ?")
    model.predict("Why not put knives in the dishwater?")
    model.test("cooking.valid")
    model.test("cooking.valid", k = 5)
    
  • precision 和 recall

    # Top 5 预测标签,用来计算 precision 和 recall model.predict(“Why not put knives in the dishwater?”, k = 5)

  • 增强模型预测能力

    • (2)数据预处理

      • 将单词中的大写字母转换为小写字母
      • 处理标点符号
      $ cat cooking.stackexchange.txt | sed -e "s/\([.\!?,'/()]\)/ \1 /g" | tr "[:upper:]" "[:lower:]" > cooking.preprocessed.txt
      $ head -n 12404 cooking.preprocessed.txt > cooking_preprocessed.train
      $ tail -n 3000 cooking.preprocessed.txt > cooking_preprocessed.valid
      
      import fasttext
      
      model = fasttext.train_supervised(input = "cooking_preprocessed.train")
      model.test("cooking_preprocessed.valid")
      
    • (2)增多 epochs

      import fasttext
      
      model = fasttext.train_supervised(input = "cooking.train", epoch = 25)
      model.test("cooking.valid")
      
    • (3)增大 learning_rate

      import fasttext
      
      model = fasttext.train_supervised(input = "cooking.train", lr = 1.0)
      model.test("cooking.valid")
      
      import fasttext
      
      model = fasttext.train_supervised(input = "cooking.train", lr = 1.0, epoch = 25)
      model.test("cooking.valid")
      
    • (4)word n-grams

      model = fasttext.train_supervised(
          input = "cooking.train",
          lr = 1.0,
          epoch = 25,
          wordNgrams = 2
      )
      model.test("cooking.valid")
      
  • Bigram

  • Scaling thing up

    model = fasttext.train_supervised(
        input = "cooking.train",
        lr = 1.0,
        epoch = 25,
        wordNgrams = 2,
        bucket = 200000,
        dim = 50,
        loss = "hs"
    )
    
  • 多标签分类(Multi-label classification)

    import fasttext
    
    model = fasttext.train_supervised(
        input = "cooking.train",
        lr = 0.5,
        epoch = 25,
        wordNgrams = 2,
        bucket = 200000,
        dim = 50,
        loss = "ova"
    )
    
    model.predict(
        "Which baking dish is best to bake a banana bread ?",
        k = -1,
        threshold = 0.5
    )
    model.test("cooking.valid", k = -1)
    

3.使用 fastText 进行词表示

图像数据处理–Pillow

1.Pillow 概览

PIL: Python Imaging Library,Python 图像处理库,提供了如下的功能:

  • Image Archives, 图像存档和批处理

    • 图像读取(read image)
    • 图像打印(print image)
    • 图像缩略图(create thumbnails)
    • 图像格式变换(convert file format)
  • Image Display, 图像显示

    • TK PhotoImage
    • TK BitmapImage
    • Windows DIB interface[PythonWin, Windows-based tookits]
    • show[debugging]
  • Image Processing, 图像处理

    • 图像点操作(point operations)
    • 图像卷积核滤波(filtering with a set of build in convolution kernel)
    • 图像颜色空间转换(image colour space conversions)
    • 图像缩放(image resizing)
    • 图像旋转(image rotation)
    • 图像仿射变换(image arbitray affine transforms)
    • 图像统计信息分析直方图(histogram method)

2.Pillow 安装

  • macOS 安装:

    $ python3 -m pip install --upgrade pip
    $ python3 -m pip install --upgrade Pillow
    
  • Windows 安装:

    $ python3 -m pip install --upgrade pip
    $ python3 -m pip install --upgrade Pillow
    
  • Linux 安装:

    $ python3 -m pip install --upgrade pip
    $ python3 -m pip install --upgrade Pillow
    

3.Pillow 核心

3.1 Image class

API:

  • from PIL import Image
  • Image.open
  • Image.format
  • Image.size
  • Image.mode
  • Image.show

示例:

import os
from PIL import Image

image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
image_name = "lena"
try:
    with Image.open(os.path.join(image_path, image_name + ".png")) as im:

        # 图像格式
        print(im.format)

        # 图像尺寸
        print(im.size)

        # 图像模式
        print(im.mode)

        # 图像打印
        im.show()
except IOError as e:
    print(f"Can't open {image_name}")
_images/lena.png

Note

  • .format: 图像格式
  • .size: 图像尺寸(width_pixels, height_pixels)
  • .mode: 图像中条带的数量和名称、像素类型、像素深度

3.2 图像读、写、转换

API:

  • Image.open
  • Image.save
  • Image.thumbnail(size = (width, height))

示例:

  • 将图像转换为 JPEG 格式:

    import os, sys
    from PIL import Image
    
    for infile in sys.argv[1:]:
        f, e = os.path.splitext(inflie)
        outfile = f + ".jpg"
        if infile != outfile:
            try:
                with Image.open(infile) as im:
                    im.save(outfile)
            except OSError:
                print("cannot convert", infile)
    
    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            im.save(os.path.join(image_path, image_name + ".jpg"))
    except IOError as e:
        print(f"Can't open {image_name}")
    
    _images/lena.jpg
  • 创建 JPEG 缩略图(thumbnails)

    import os, sys
    from PIL import Image
    
    size = (128, 128)
    
    for inflie in sys.argv[1:]:
        outfile = os.path.splitext(infile)[0] + ".thumbnail"
        if infile != outfile:
            try:
                with Image.open(infile) as im:
                    im.thumbnail(size)
                    im.save(outfile, "JPEG")
            except OSError:
                print(f"Can't create thumbnail for{infile}")
    
    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    size = (128, 128)
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            im.thumbnail(size)
            im.save(os.path.join(image_path, image_name + ".JPEG"))
    except IOError as e:
        print(f"Can't open {image_name}")
    
    _images/lena.JPEG
  • 识别图像文件

    import sys
    from PIL import Image
    
    for infile in sys.argv[1:]:
        try:
            with Image.open(infile) as im:
                print(infile, im.format, f"{im.size}x{im.mode}")
        except OSError:
            pass
    
    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            print(image_name, im.format, f"{im.size}x{im.mode}")
    except IOError as e:
        print(f"Can't open {image_name}")
    

3.3 图像剪切、粘贴、拼接

API:

  • Image.crop: 从图像中复制子矩形
  • Image.past
  • Image.merge
  • Image.split
  • Image.transpose(Image.ROTATE_180)

示例:

  • 从图像中复制子矩形

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        box = (100, 100, 200, 200)
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            region = im.crop(box)
            region.show()
            region.save(os.path.join(image_path, image_name + "_region" + ".png"))
    except IOError as e:
        print(f"Can't open {image_name}")
    
    _images/lena_region.png

    Note

    • PIL 中图像左上角坐标为 (0, 0)
    • box(left, upper, right, lowe)
  • 从图像中复制子矩形、将子矩形粘贴回去

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        box = (100, 100, 200, 200)
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            region = im.crop(box)
            region = region.transpose(Image.ROTATE_180)
            im.paste(region, box)
            im.save(os.path.join(image_path, image_name + "_region_paste" + ".png"))
    except IOError as e:
        print(f"Can't open {image_name}")
    
    _images/lena_region_paste.png
  • 图像滚动(image roll)

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    def roll(image, delta):
        """Roll an image sideways"""
        xsize, ysize = image.size
        delta = delta % xsize
        if delta == 0:
            return image
        part1 = image.crop((0, 0, delta, ysize))
        part2 = image.crop((delta, 0, xsize, ysize))
        image.paste(part1, (xsize - delta, 0, xsize, ysize))
        image.paste(part2, (0, 0, xsize - delta, ysize))
    
        return image
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            im = roll(im, 10)
            im.save(os.path.join(image_path, image_name + "_roll" + ".png"))
    except OSError:
        print(f"cannot open {image_name}")
    
    _images/lena_roll.png
  • RGB波段拆分、合并

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            r, g, b = im.split()
            im = Image.merge("RGB", (b, g, r))
            im.save(os.path.join(image_path, image_name + "_merge_gbr" + ".png"))
    except OSError:
        print(f"cannot open {image_name}")
    
    _images/lena.png _images/lena_merge_rbg.png _images/lena_merge_brg.png _images/lena_merge_bgr.png _images/lena_merge_grb.png _images/lena_merge_gbr.png

    Note

    • 对于单波段图像(single-band),Image.split 返回图像本身
    • 为了对单个颜色波段进行处理,需要首先将图像转换为 RGB

3.4 图像几何变换

API:

  • Image.Image.resize
  • Image.Image.rotate
  • Image.transpose
  • Image.transform

示例:

  • 简单的几何变换-改变图像像素大小

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            out = im.resize((1000, 1000))
            out.save(os.path.join(image_path, image_name + "_resize" + ".png"))
    except OSError:
        print(f"cannot open {image_name}")
    
    _images/lena_resize.png
  • 简单的几何变换-图像逆时针旋转一定的角度

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            out = im.rotate(45)
            out.save(os.path.join(image_path, image_name + "_rotate" + ".png"))
    except OSError:
        print(f"cannot open {image_name}")
    
    _images/lena_rotate.png
  • 图像转置

    import os
    from PIL import Image
    
    image_path = "/Users/zfwang/project/machinelearning/deeplearning/deeplearning/src/pillow_src/images"
    image_name = "lena"
    
    try:
        with Image.open(os.path.join(image_path, image_name + ".png")) as im:
            out1 = im.transpose(Image.FLIP_LEFT_RIGHT)
            out2 = im.transpose(Image.FLIP_TOP_BOTTOM)
            out3 = im.transpose(Image.ROTATE_90)
            out4 = im.transpose(Image.ROTATE_180)
            out5 = im.transpose(Image.ROTATE_270)
            out1.save(os.path.join(image_path, image_name + "_rotate_1" + ".png"))
            out2.save(os.path.join(image_path, image_name + "_rotate_2" + ".png"))
            out3.save(os.path.join(image_path, image_name + "_rotate_3" + ".png"))
            out4.save(os.path.join(image_path, image_name + "_rotate_4" + ".png"))
            out5.save(os.path.join(image_path, image_name + "_rotate_5" + ".png"))
    except OSError:
        print(f"cannot open {image_name}")
    
    _images/lena.png _images/lena_rotate_1.png _images/lena_rotate_2.png _images/lena_rotate_3.png _images/lena_rotate_4.png _images/lena_rotate_5.png

Note

  • trasnpose(ROTATE)Image.Image.rotate 效果相同
  • transform() 能进行更多形式的图像转换

3.5 图像颜色转换

3.6 图像增强

3.7 图像序列

3.8 PostScript 打印

3.9 图像读取

3.10 控制图像编码

4.Pillow API

  • Module

    • Image
    • ImageChops
    • ImageCms
    • ImageColor
    • ImageDraw
    • ImageEnhance
    • ImageFile
    • ImageFilter
    • ImageFont
    • ImageGrab
    • ImageMath
    • ImageMorph
    • ImageOps
    • ImagePaletee
    • ImagePath
    • ImageQt
    • ImageSequence
    • ImageShow
    • ImageStat
    • ImageTK
    • ImageWin
    • ExifTags
    • TiffTags
    • JpegPressets
    • PSDraw
    • PixelAccess
    • PyAccess
    • features
  • PIL Package

    • PIL
  • Plugin reference

  • Internal Reference Docs

图像数据处理–OpenCV

1.OpenCV 概览

2.OpenCV 安装

3.OpenCV 核心