Deep Learning for Computer Vision with Python
三个模块解读
- 
几个比较刷新认知的点:
 - 
0. 介绍
 - 
- 0.1 书本类容
 - 0.2 工具
 
 - 
- Starter Bundle
 
- 
Image Fundamentals
- 
Image Composition Basics: Pixels (pixel)
- Constructing an Image from Color Channels
 - Representation of Images in Python: NumPy Arrays
 - Comparison between RGB and GBR color spaces
 - Resizing and aspect ratio considerations
 
 - 
数据输入
 - 
从 K-NN 到 参数学习
 - 
优化方法和正则化
 - 
- 优化方法
 - 正则化
 
 - 
神经网络
 - 
- 激活函数
 - (前馈)神经网络
 
 - 
神经学习
 - 
感知器
 - 
卷积神经网络
 - 
- 理解卷积
 
 - 
卷积 vs 互相关
 - 
卷积神经网络
 - 
- 层的类型
 
 - 
一般架构
 - 
- 层的堆叠模式
 - 经验法则
 - CNN是否不变于平移,旋转和缩放?
 
 - 
Keras 实践:leNet、mini_vgg
 - 
- 如何分析 loss-acc 图
 - 学习率
 
 - 
欠拟合和过拟合(深度学习领域)
 - 
- 学习率的影响
 
 - 
几个工具
 - 
- Callback
 - Checkpointing
 - Architecture visualization
 
 - 
State-of-the-art CNNs in Keras
 - 
- 参数学习
 
 
 - 
 - 
2. Practitioner Bundle
 - 
- 图片处理
 - 
- 各种导入方法的区别
 
 
 - 
2.1 提升准确率的多种方法
 - 
- 
迁移学习
 - 
集成方法
 - 
正则化方法
 - 
- 数据增强
 
 - 
高级优化算法
 - 
- 适应性学习率方法
 
 - 
深度学习的原料和菜谱
 - 
- 四种原料:
 - 深度学习菜谱:(Andrew Ng)
 
 - 
使用迁移学习还是从头训练?
 
 - 
 
 - 
2.2 更大数据集与更复杂的网络架构
- 
- 
新增了两种图像预处理方案
 - 
均值减去操作作为预处理器
- Patch Preprocessing(分块处理)
 - Trim Processing(裁剪处理)
 
 - 
GoogLeNet
 - 
- Inception 模块
 - mini_GoogLeNet
 
 - 
ResNet
 - 
- Residual 模块
 - ResNet
 
 
 - 
 
 - 
 - 
3. ImageNet数据集
- 
- 
3.1 基于ImageNet的数据集构建
 - 
- 高性能计算设备
 - 收集并预处理ImageNet数据集
 - 利用收集并预处理好的ImageNet数据集构建模型训练过程
 
 - 
3.2 深度学习的实际应用
 - 
- 情绪识别
 
 
 - 
 - 
总结
 - 
- 参数
 
 - 
附录
 
 - 
 
几个比较刷新认知的点:
- 参数学习(而不是模型学习)
核心目标在于确定能够代表模型的关键parameter ,这些parameter 的选择直接关系到整个系统的性能。
其中最为核心的是"model parameter"的选择。
在可调parameter中包含了多个关键因素:
(可调parameter)
① 学习率(是最重要parameter) 在一定程度上决定了优化过程的速度。
② 正则化机制不仅有助于防止过拟合问题还能够引导优化器选择更适合的数据分布区域。
事实上是从满足训练loss低的很多种parameter中_选出最优__parameters__ 并且甚至可能以提高训练loss为代价来提高测试 loss。
而batch大小直接影响着每次迭代过程中所处理的数据样本数量。 
基于损失函数图像(而非准确率)来评估模型性能时会发现许多问题
损失函数图像能够更好地反映模型的学习状态
- 神经元作为二元输出单元,在深度学习框架中通常通过激活函数来实现其功能/激活过程
- 在深度学习框架中,通常将这一机制称为层
 - 预训练模型在实际应用中与理论研究中有何区别 (理解误区)
 
 
0. 介绍
0.1 书本类容
Starter Bundle (初级篇)
学习基础知识:
 * 机器学习
 * 神经网络
 * 卷积神经网络
 * 在自定义数据集上的运用
        Practitioner Bundle (中级篇) 深入掌握深度学习的核心内容,并掌握高阶技能以探索实操策略与实践经验。
ImageNet Bundle(高级篇)
0.2 工具
主要三个模块:
- 
Python
 - 
Keras
 - 
Mxnet(高级篇)
其它: - 
OpenCV
 - 
scikit-image
 - 
Scikit-learn
 - 
…
 
1. Starter Bundle
图像基础
图像构成的基础:像素(pixel)
通常来说,在图像中指定位置出现的光被视作'色彩'或'亮度'。
在灰度图中,
每一个像素位于0到255之间的实数值范围内。
其中,
0对应于黑色,
而255则对应于白色。
其他数值则对应不同程度的灰色。

彩色图像通常在RGB颜色空间中表示(当然还有其它的颜色空间)。在RGB色彩空间中,
像素不再像灰度/单通道图像中那样是标量值-而是由三个值的列表表示像素:一个代表Red元素,
一个代表Green元素还有另一个代表Blue元素。
为了在RGB颜色模型中定义颜色,
我们需要定义单个像素中包含的红色绿色和蓝色的数量。
每个Red Green Blue通道都可以具有在一个0到255范围内定义的值,
总共256个"shades"其中0表示无表示而255表示完整表示。
考虑到像素值仅需要在一个0到255范围内,
我们通常使用8位无符号整数来表示强度。

…
Forming an Image From Channels
我们可以通过概念化的方式理解RGB图像。该图像由宽度为W、高度为H的三个独立矩阵构成,并且每个RGB分量对应一个。如图3.5所示。通过结合这三个矩阵可以形成形状为W×H×D 的多维数组其中变量D表示通道的数量或深度(在RGB空间中,默认值为3)。

图像在python中的表示:NumPy array
- NumPy :(height, width, depth)
 
关于NumPy中为什么把高放在前面:
When determining a matrix's dimensions, it's customary to express it as rows multiplied by columns. The image's height corresponds to its row count, while its width equals the column count. The depth remains unchanged.
RGB vs GBR
需要注意的是,在OpenCV中按照blue, green, red(GBR)的顺序排列像素值(而不是RGB)。——历史原因
缩放和 宽高比( aspect ratio)
注意:忽略宽高比(aspect ratio)可能会导致图像看起来 压缩和变形
From an aesthetic standpoint, one typically prefers maintaining an image's aspect ratio when resizing it – though this principle doesn't apply universally in deep learning scenarios. The majority of neural networks and convolutional neural networks (CNNs) used for image classification tasks typically operate with fixed-size inputs. This means that all images passed through such networks must adhere to consistent dimensions. Commonly encountered input sizes for CNNs often include options like 32x32 pixels, progressing up to larger configurations such as 64x64, 299x299, or even wider ones like 518x518 pixels.
数据输入
输入大小固定
必要性
机器学习算法模型必须具备统一维度的特征向量。从而要求所有图像都经过预处理,并调整至一致的宽度与高度。
-方法
① 多种高级方法在兼顾纵横比( aspect ratio)的基础上进行图像转换;
② 一些情况下为了加快效率采取了直接粗暴的方式进行转换(完全忽视了纵横比( aspect ratio))。
决定因素在于方差因子的复杂程度:在某些情况下,可以忽略长宽比例的效果;而在其他情况下,则必须保留长宽比例。
代码
① preprocess an image:
(simple_preprocessor)
    import cv2
    class SimplePreprocessor:
     def __init__(self, width, height, inter=cv2.INTER_AREA):
          # store the target image width, height, and interpolation
          # method used when resizing
          self.width = width
          self.height = height
          self.inter = inter
     def preprocess(self, image):
          # resize the image to a fixed size, ignoring the aspect
          # ratio
          return cv2.resize(image, (self.width, self.height),
               interpolation=self.inter)
        (这里只是处理单张图片-没有考虑导入整个数据)
② load a collection of images from disk
(simple_dataset_loader)
    import os
    import cv2
    import numpy as np
    
    
    class SimpleDatasetLoader:
    # Method: Constructor
    def __init__(self, preprocessors=None):
        """
        :param preprocessors: List of image preprocessors
        """
        self.preprocessors = preprocessors
    
        if self.preprocessors is None:
            self.preprocessors = []
    
    # Method: Used to load a list of images for pre-processing
    def load(self, image_paths, verbose=-1):
        """
        :param image_paths: List of image paths
        :param verbose: Parameter for printing information to console
        :return: Tuple of data and labels
        """
        data, labels = [], []
    
        for i, image_path in enumerate(image_paths):
            image = cv2.imread(image_path)
            label = image_path.split(os.path.sep)[-2]
    
            if self.preprocessors is not None:
                for p in self.preprocessors:
                    image = p.preprocess(image)
    
            data.append(image)
            labels.append(label)
    
            if verbose > 0 and i > 0 and (i+1) % verbose == 0:
                print('[INFO]: Processed {}/{}'.format(i+1, len(image_paths)))
    
        return (np.array(data), np.array(labels))
        ③ more advanced dataset loaders
从 K-NN 到 参数学习
参数估计
K-NN 算法本质上不进行参数估计的过程,在训练阶段它仅存储训练样本信息,并通过邻近区域的数据点对新样本进行分类或回归决策。
由于该方法本身不具备优化机制,在出现预测错误时无法通过优化模型结构来提升性能;其计算规模与特征空间维度呈正相关关系。
替代现有方案的同时,在更好的方法下(即通过开发一种更加高效的算法),我们可以构建一个机器学习模型,在训练期间从我们的输入数据中学习特征模式(这要求我们在训练过程中投入更多的人力成本)。然而这种方案具有以下优势:使用少量参数进行建模(相较于传统的基于大量样本的学习方式),我们可以仅依赖于这些参数来进行表达而非依赖于大量的训练样本数量)。其中一种方法被称为参数化建模
"A learning model that represents data using a fixed-size set of parameters (regardless of the amount of training data) is termed a parametric model. Regardless of the volume of data input, such models steadfastly retain their original stance on required parameter count." – Russell and Norvig (2009) [73]
基于参数化方法(parameterized approach) ,我们能够通过对输入数据的学习 来识别潜在的规律 。
- 参数学习四大块:
 
- Data
 - Scoring function
 - Loss function
 - Weights and biases
 
优化方法和正则化
优化方法
- GD
 
现有梯度下降算法在完成整个训练数据集的训练后才进行一次权重更新(每一轮epoch)。该方法显得效率低下且资源浪费严重。
_Parameter*:learning rates
Gradient descent is controlled by the learning rate.
The learning rate is currently the most important core parameter for training models:
当学习率过高时,可能导致模型无法收敛;而当学习率过低时,则会增加训练时间以使损失函数达到一个较为合理的结果。
- SGD
 
(并不需要经过一次 epoch,而是)每隔一个 batch 就更新一次权重
参数 : batch size
Rather than calculating the gradient across the entire dataset, we instead sample our data to create a batch. We then calculate the gradient on this batch and adjust the weight matrix W accordingly.
纯SGD的核心理念在于使用单样本实现参数更新。具体而言,在每次迭代中随机从训练集中选取一个样本,并基于此样本计算当前批次的梯度后完成权重更新;而常规做法则是采用较大的批量尺寸(batch size),通常选择值如32、64、128或256等具有较高计算效率的数值。其核心优势主要体现在两个方面:首先,在参数更新过程中通过减少梯度估计的方差来提升训练稳定性;其次,在批量大小设计上选择批大小为二进制数(power of two)的形式有助于加速优化过程(improve the optimization efficiency of linear optimizers)
- Momentum
 - Nesterov’s Acceleration
 - Anecdotal Recommendations
 
正则化
定义
以增加训练误差为代价以减少测试误差的一些策略。
A variety of machine learning strategies are specifically developed to minimize test errors, while attempting to balance performance against increased training errors.
These approaches are commonly referred to as regularization techniques.– Goodfellow et al. [10]
该方法旨在确定一组合适的参数(以提升模型的泛化能力)。为了实现这一目标,我们需要采用正则化技术。
The team must be aware that we are operating within a real-valued space, which implies the existence of an uncountable number of parameters capable of achieving adequate classification performance on the dataset (for some definition of "adequate").
What steps should we take to select a set of parameters that ensure our model generalizes well? To begin with, it's essential to minimize the effects of overfitting. The solution lies in applying regularization techniques. Second only to your learning rate, regularization is the most critical parameter you can adjust.
透彻理解 损失函数 能助我们筛选出在训练集中展现出色的参数"它"们;而通过引入正则化技术则能从这些候选参数中进一步选出对未知测试集表现优秀的参数。
正则化的手段
权重衰减 (作用于损失函数):比如 L1 和 L2
例子:
参数-penalty : None 、l1、l2
    for r in (None, "l1", "l2"):
    	model = SGDClassifier(loss='log', penalty=r, max_iter=10,
    	learning_rate="constant", eta0=0.01, random_state=42)
    	model.fit(trainX, trainY)
        作用于模型:比如 Dropout
暗藏在训练过程中,比如数据增强和”早停“
神经网络

一个简单的神经网络(NN),其结构基于输入x与权重w之间的线性组合进行计算。接着, 该线性组合的结果通过指定的激活函数进行处理, 并以此判断神经元是否被激活。(一个神经元仅限于两种输出状态: 兴奋或抑制! 其输出状态完全由激活函数来决定)
激活函数
涵盖两大类方面:
传统类:阶跃函数、sigmoid函数、tanh函数…
此外,在现代类中包含了ReLU、Leaky ReLU和ELU等激活函数

(附录中 还包含更详细的信息)
选择时应考虑的因素包括:首先建议从标准的ReLU架构开始设置网络参数;然后优化其他相关网络参数设置;之后逐步引入其他变体类型。
(前馈)神经网络

(传统的前馈神经网络所有的层都是全连接层- fully- connected (FC) layer)
第0层包括3个输入变量xi(i=1,2,3)。这些输入可以表示为图像的原始像素强度或基于图像提取的特征向量。
第1层和第2层层别地包含2个和3个隐含节点。
第3层为输出层或可见层,在此我们能够通过网络获得所有可能的分类结果。
(输出层中的节点数量与可能的分类结果数量相等)
问题:
前馈网络每层神经元的选择:
注意:
连接只能由前一层神经元节点传到后一层节点;同层节点之间没有连接
输入层的神经元没有激活函数,除此之外的神经元均有激活函数。
神经网络的第一层单元。该单元接收输入信号(值)并通过连接传递到下一层单元,并不对其输入信号(值)执行任何运算操作。该单元也没有自身设定的权重参数和偏置参数。
神经学习
神经学习是指定义网络中节点之间的权重和连接的方法。
"When the axon of cell A comes sufficiently close to excite cell B, this interaction occurring repeatedly or persistently leads to growth processes and metabolic changes in one or both cells. These changes result in an enhanced efficiency for cell A, functioning as a participant in B's firing activity." – Donald Hebb [105]
在人工神经网络中 这一原理表明 在相同的输入情况下 相关联输出节点间的连接强度会增强 我们可以称这种现象为相关性学习(correlation learning) 其原因在于 神经元之间连接强度反映了输出的相关程度
感知器

    # import the necessary packages
    import numpy as np
    class Perceptron:
     def __init__(self, N, alpha=0.1):
          # initialize the weight matrix and store the learning rate
          self.W = np.random.randn(N + 1) / np.sqrt(N)
          self.alpha = alpha
        …
M-P模型与感知器:
M-P模型即为目前所采用的一种神经元结构,在其设计中未包含参数的学习机制。而单层感知机则引入了损失函数这一概念,并首次提出了一种学习的方法。相比之下多层感知机通过增加层数来解决非线性问题但其局限性在于仍需手动固定一层参数仅能训练其中一层网络直至1986年Hinton提出反向传播算法使得深度网络的学习成为可能。
随着GPU并行运算能力的快速发展网络架构得以不断优化升级并在这一过程中涌现出越来越多的新网络模型其中感知机作为基础模型逐渐被更为复杂的深层网络取代
卷积神经网络
理解卷积
除末端之外的全连接层替换成卷积层
非线性激活函数通过持续施加于各卷积操作之间的各层来实现。
ReLU等非线性激活函数被用于在经过各次卷积操作后的各卷积层之间以及在最终的一到两层全连接_layer_末部作为输出。
此外,在这些中间处理过程中中使用的是为了减少输入的空间维度并防止过拟合。
A non-linear activation function, including ReLU, is subsequently applied to the output from these convolutions. The procedure then continues on (accompanied by a mixture of other layer types) to diminish both the width and height dimensions of the input volume while assisting in reducing overfitting tendencies. These processes are eventually reached at the conclusion stage where typically one or two fully connected (FC) layers are added to produce our final output classifications.
卷积神经网络 的两个好处:
- 局部不变性(local invariance):通过池化操作实现。
过滤器会遍历整个图像,并无需过分关注整体图像的位置。
在现实中进行pooling操作时,
平移、旋转和缩放等变换下,
图像是会保持不变的。
其中后者的效应更为显著。 
特性(局部特性),每个滤波器捕获了图像的部分区域;这些部分区域结合形成了整体图像。
卷积 vs 互相关
卷积与互相关等同于彼此(通常指代同一个概念)
卷积即为两个矩阵对应元素相乘后再累加求和的结果。
图像:“大矩阵;核:“微小矩阵”

红色:核
白色:图像矩阵
核的大小为任意大小 MxN ,只要求 M 和N 都是奇数 。
取为方阵,是为了利于后面的优化计算;
取奇数,是为了表示核的位置
Typically, the kernels employed in deep learning and convolutional neural networks (CNNs) are often N × N square matrices. This configuration enables efficient utilization of optimized linear algebra libraries designed for processing such square matrices.

- sliding (滑动)
探讨卷积核在图像矩阵滑动过程中所扮演的角色及其重要性:该操作不仅降低了输入图片的空间维度(spatial dimension),还通过减少细节信息的冗余来增强特征提取的效率。
- padding(填充)
当不想减少输入图片尺寸的时候可以使用填充。 

卷积神经网络
CNNs:学习输入图片并决定使用那些卷积核的算法。
理解CNN如何实现,需要思考的两个问题:
1. 层的类型?
2. 层的堆叠方式?
相较于传统的人工神经网络而言,卷积神经网络具有三维空间结构:包括宽度、高度以及深度。
第三个维度-深度,代表图片的”通道“维度 或者表示层中卷积核的数量 。
考虑到卷积网络的输入维度为 Weight \times Height \times Depth ,(尚未包含后续的隐藏层和输出层)因此通常需要大量节点数量,并会产生大量参数数量。 尽管CNN具有局部感受野特性(local connectivity),这种特性能够有效缓解部分过高的计算复杂度问题。
层的类型

(有些不是真正的layer)
- 仅包含参数的为卷积层和全连接层 (或按小层面来说 BN 也算)
- 在网络架构设计中最重要的模块是卷积模块、池化模块、激活函数以及全连接模块(其它组件都需依赖这些基本单元)
 - 池化操作与其它两种结构同样关键,并经常被整合进模型可视化图谱中
 - 在某些情况下这些辅助组件(如激活节点)虽不构成真正意义上的"模块"但会被纳入网络架构图以便于更详细地展示系统工作流程
 - 激活操作本身并非传统意义上的网络结构单元(因为它们不具备可学习的参数或权重)
 - 这种类型的节点通常不会被包含在标准架构图中因为它们的作用被认为已经被后续过程所涵盖
 
 
(1)卷积层 每个卷积核所对应的输入特征图或图片深度一致,并且每层输出结果即为多个独立的卷积操作结果叠加而成。 提醒:接收的原始图像或特征图具有特定结构
(2)激活层
实际上, 激活函数被视为体系架构的核心组件. 在构建CNN模型时, 我们通常会从表/图中省去激活层以节省空间. 然而, 激活层却被默认视为体系架构的一部分. 将( INPUT → CONV → RELU → FC)重构为(INPUT → CONV → FC).
但是在这里,作者并不认同这种简化的处理方式:激活层仅以逐元素的方式应用激活函数到输入数据上,并且确保输出的宽度、高度和深度与输入完全相同——具体而言是 Win put = Wout put ,Hinput = Houtput ,Dinput = Doutput 。
(3)池化层
分类
Maximum pooling被应用于中间区域以降低输出的空间维度;而Average pooling则被应用于后端部分以防止仅通过全连接层(FC)进行特征提取
通常是最大池化技术最为常用,在面对伴随越来越多特殊的细节元素时(即当越来越多独特的微小结构被引入系统),这一趋势正逐渐发生转变。
池化后的输出尺寸:
(其中:
- S表示stride即步长;
 - F代表池化核的尺寸;输入大小)
计算方式如下:
W_output 等于 ( (W_input - F) 除以 S ) 加 1;
H_output 等于 ( (H_input - F) 除以 S ) 加 1;
深度维度 D_output 保持不变。 
常见的最大池化结构:
① F=3,S=2(重叠池化)常用于处理图像数据或高维数据的情况;
② F=2,S=2(非重叠池化)是最常用的设计方案之一,并广泛应用于较小尺寸图片的处理中
研究热点:池化层还是卷积层?越来越多的网络架构选择放弃传统的池化层,在这种设置下,在网络的输出端采用平均池化操作。
(4)全连接层
FCs 通常用在卷积层之后、softmax 分类器之前 ,例如下面的结构:
INPUT = > CONV => RELU => POOL => CONV => RELU => POOL => FC => FC
批标准化层(BN层)
In 2015, whose work Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift was published, BN layers have since been applied in both layer-to-layer and within-layer contexts for normalizing activation operations across each batch. Similarly, BN also applies input normalization to convolutional layers and their activations within each batch.
此层通过将前一层的激活值在每一个批次的数据中进行归一化处理,使其输出数据的均值趋近于0并保持标准差接近1。
作用
尽管没有减少调整参数的需求, 但批标准化通过减少学习率与正则化强度的变化幅度, 并使得这两个因素更容易进行调节。
- 减少了训练的 epoch 数
 - 稳定训练:允许学习率和正规化更大的变化强度。
 
自然地产生一种疑问:归一化与标准化的意义何在? (参考「高屋建瓴机器学习/深度学习」)
潜在问题:可能会延长训练的实际时间两到三倍(尽管迭代次数有所减少,但每一批的数据都需要处理)
建议:任何情况下都可以使用——避免过拟合和获得更好的准确率。
BN 使用位置?
① BN 用在激活层之前(原作者),类似以下结构:
INPUT = > CONV => BN => RELU …
We introduce the BN transform right before the nonlinearity, where we normalize x = Wu + b.
② BN 用在激活层之前(Keras 之父),类似:
INPUT = > CONV => RELU => BN …
According to my understanding, recent code written by Christian Szegedy, referencing his work in the BN paper, applies ReLU prior to the BN layer. Despite this, it remains occasionally a point of contention within the community.
很多事实证明,BN 用在激活层之后准确率会更高。
(5)Dropout 层
Dropout 实际上是一种防止模型过拟合的关键正则化技术之一——它通过在每次训练的小批量数据中以概率p随机关闭一些来自前一层的输入特征来实现对神经网络参数的有效约束。

通常情况下,在多数场景中我们选择参数 p 为 0.5,并将其放置于最后几个全连接层之间(其中最后一个全连接层被设计为 softmax 分类器)。
… CONV = > RELU => POOL => FC => DO => FC => DO => FC
然而,在某些情况下(通常伴随着下采样操作或最大池化等后续操作),作者指出也可以采用p=0.10-0.25这一参数范围应用于较早的位置进行处理
一般架构
如上所说:
- 现代神经网络框架如CNN主要由四类基本功能模块组成:卷积层、池化层、激活函数以及全连接层;
- 其中只有卷积神经网络模型中的卷积层与全连接层数组完成真正负责特征提取与参数更新的任务(即Batch Normalization),而其他辅助操作如加性归一化等则仅执行单一特定功能;
 - 激活操作与Dropout机制虽然通常不被视为传统意义上的‘神经元层次’**[1]或‘权重层次’[2]**但它们一般都会包含在CNN架构图中以便于操作流程更加清晰明了。
 
 
层的堆叠模式
目前为止来说,最常用的 CNN 模型结构是由一系列卷积层和激活函数堆叠而成,通常紧跟池化操作。不断重复这一过程直至 volume 的空间维度足够小以至于能够与全连接层进行链接。
因此,最常见的CNN框架如下所示:
*INPUT => [[CONV => RELU]*N => POOL?]*M => [FC => RELU]K = > FC
常见选择:
• 0<=N<=3
• M>=0
• 0<=K<=2
一般来说,如果有以下情况出现,我们将考虑更深的神经网络:
- 有大量的标记训练数据集
 - 分类问题非常具有挑战性
 
在连接池化操作之前,在ImageNet板块中将会介绍更多的非传统变种
经验法则
经验:
- 输入图片的大小必须是方形的(square) 。
 
By using square inputs, we can leverage linear algebra optimization libraries. Common input layer sizes such as 32 \times 32, 64 \times 64, 96 \times 96, 224 \times 224, 227 \times 227, and 255 \times 184 are frequently employed in convolutional neural networks (for brevity, channel numbers have been omitted).
- 在第一个卷积操作之后,输入的层也应该被2整除。
 
一般来说,卷积核的大小应该都是 3x3 和5x5 的。
1x1 的卷积被用作提取本地特征,但只用在更为高级的网络结构中。
例如,在模型首个卷积层中(当输入图片尺寸超过200x200像素且需减少输入空间大小时),可以部署7x7或11x11的较大尺寸卷积核;然而,在此初始CONV层之后若过滤器尺寸急剧下降,则会导致迅速降低体积的空间尺寸。
通常情况下,在深度神经网络中使用CNN模型时,默认设置下会将卷积核的步长参数设为1(尤其当输入图像较小时)。这种配置有助于该CONV层提取特征,并且池化层则用于实现下采样功能。需要注意的是,并非所有情况都采用这种结构;例如,在某些网络架构中可能会采用移动滤波器来替代最大值池化操作
几条建议:
在应用到CONV层时使用zero padding以使输出尺寸与输入尺寸一致。(Keras会自动计算zero padding)
主要采用池化层而非卷积层以降低输入的空间分辨率(随着更多经验的积累,我们可以考虑反向操作)。
常见的做法是采用尺寸大于 2 \times 2 的最大池化感受野,并且当 S=2时更是常见。有时也会在神经网络初期出现一个 3 \times 3 的感受野来帮助降低输入空间的规模。强烈不建议让池化层的感受野超过 3 \times 3 的尺寸,因为这类操作会对输入造成负面影响。
批归一化(BN)是一种耗时的操作(所需时间是训练CNN的2-3倍),尽管如此,在任何情况下都建议采用它。另外强调应将批归一化操作放置在激活函数之后。
Dropout常被应用于全连接层之间作为正则化技术,在其中常见的取值是p=0.5的情况下表现良好;建议在每个网络结构中尝试应用该技术以提升模型泛化能力;此外,在某些模型架构中采用较低p值(如p=0.1至p=0.25)的应用也被提出
CNN是否不变于平移,旋转和缩放?
Keras 实践:leNet、mini_vgg
如何分析 loss-acc 图
看下面的 欠拟合和过拟合
学习率
固定采用恒定的学习率可能使得当损失函数值已经接近最小值时难以寻找到更优的解决方案。
- 学习率策略:先高后低
 - 降低学习率的两种策略
 
- 
随着 epoch 数的增加而逐渐减少
设置 decay = lr / epoch - 
在特定的 epoch处减少(比如基于分段函数)
需要自定义一个。 
欠拟合和过拟合(深度学习领域)
- 训练模型的目标
 
- 通过最大化训练损失的减少来实现模型优化
 - 保证训练损失与测试损失之间的差距较小
 

注意:是**通过 Loss (而不是acc)**和模型能力的函数图像来判断!
- 如何避免过拟合
 
- 减少模型复杂性(采用拥有较少层数和神经元数量的浅层架构)
 - 应用正则化技术
然而,那种方法更适合于训练数据量有限的情况。我们建议采用以下几种常见的正则化手段:例如权重衰减、Dropout机制以及数据增强技术。 
学习率的影响

几个工具
Callback
模型训练可视化
Checkpointing
- 检查模型的训练效果
 - 检查最佳模型
 
Architecture visualization
- plot_model
 
State-of-the-art CNNs in Keras
参数学习
一个问题:
I am not equipped with an expensive GPU. These deep learning networks can be effectively utilized for various purposes, particularly when dealing with significantly larger datasets than those we've analyzed in this book.
强化对参数学习 的理解:

最终实际模型的实际规模主要由参数(而非训练数据)为核心要素所决定 —— 比如说,在经过1亿张图片或仅数百张图片的训练后,VGG或ResNet 的模型规模保持不变
在反向传播算法中使用GPU可以使前向与反向传播均得到加速。一旦网络训练得当,则仅需执行前向传播来进行输入图片的分类。
在反向传播算法中使用GPU可以使前向与反向传播均得到加速。一旦网络训练得当,则仅需执行前向传播来进行输入图片的分类。
具有参数配置的模型可以直接应用于预测任务;
比如,在Python中使用image = np.expand_dims(image, axis=0)这一代码可用于实现特征x的维度提升-非常便捷地将图像输入固定尺寸预训练模型(值得探讨这种方法与通过列表构建训练集中的x及y并进行批量处理有何不同之处)
    image = load_img(args["image"], target_size=inputShape)
    image = img_to_array(image)
    
    # Our input image is now represented as a NumPy array of shape (inputShape[0], inputShape[1], 3) however we need to
    # expand the dimension by making the shape (1, inputShape[0], inputShape[1], 3) so we can pass it through the network
    image = np.expand_dims(image, axis=0)
    
    # Pre-process the image using the appropriate function based on the model that has been loaded
    image = preprocess(image)
    
    # Classify the image
    preds = model.predict(image)
        2. Practitioner Bundle
图片处理
各种导入方法的区别
当设置指定尺寸时,默认会以原始图像的中心位置进行裁剪,并且会预设不考虑图像的比例关系。也就是说,默认的做法会导致图像变形。因此,在追求效果的前提下建议编写自定义函数来执行这一步骤。

2.1 提升准确率的多种方法
迁移学习
- 两种迁移学习模型:
 
- 被当做 feature extractor 的是迁移网络
 - 微调过程涉及移除原网络的全连接层,并替换为新的全连接层;随后对新构建的全连接层进行参数初始化,并重新训练整个网络模型;或者更新较早的层。
 
- 两种 fine-tuning方法
 
固定之前的网络,仅使反向传播在新增网络上执行(继承之前训练完成的参数)
- 重新训练所有的网络
 

集成方法

正则化方法
数据增强
数据增强是针对训练集 的一种正则化操作 。
注意:只针对强训练集!
代码(来自colab)
(1)未进行数据增强
- 配置
 
    import numpy as np
    import matplotlib.pyplot as plt
    from keras.preprocessing.image import load_img
    from keras.preprocessing.image import img_to_array
    from keras.applications.imagenet_utils import preprocess_input
    
    import glob
    import imageio
    
    # # #数据1:sub_CKp (CK+子集:每种表情10人*3张)_未预处理CK+子集
    # cc_path = "/content/drive/My Drive/project/0530_TransferLearning_CNN_Facial_Expression_Recognition/my_sub_CKp/Train"
    
    # # 数据2:CK+ 
    # cc_path = '/content/drive/My Drive/dataset/CKp2'
    
    # 数据3:CK+子集(三张 + 所有人)
    cc_path = '/content/drive/My Drive/dataset/CKp'
    
    curr_cwd = cc_path
        - 准备数据 + 定义模型
 
    image_size = (224, 224)
    
    def load_ck():
    
    #To store the Features
    faces, emotions = [], []
    
    for cls in range (1,8):
        classes = ["anger","disgust","fear","happy","neutral","sadness","surprise"]    # !! 注意和文件夹中的名称对上
        for image_path in glob.glob(curr_cwd+"/"+classes[cls-1]+"/*.png"):        
                #Pre-processing
                img = load_img(image_path, target_size=image_size) ##加载图像,归一化大小    
                x_in = img_to_array(img) ##序列   (若使用 cv2.imread() 可直接变为numpynumpy数组)
                x_in = preprocess_input(x_in) ##预处理到0~1
    
                faces.append(x_in)
                emotions.append(cls-1)
        print('finished loading {} images, {} in all'.format(classes[cls-1], len(faces)))
    
    faces = np.asarray(faces, dtype='uint8')  # 转为向量
    faces = faces/255.0 # 标准化至 [0, 1]
    emotions = np.eye(7, dtype='uint8')[emotions]    # 将 y 转为one hot编码
    
    return faces, emotions
    
    
    # 建立模型
    from keras.models import Sequential
    from keras.layers.convolutional import Conv2D, MaxPooling2D
    from keras.layers.core import Activation
    from keras.layers.core import Flatten
    from keras.layers.core import Dense
    
    from keras.layers.core import Dropout
    from keras.layers.normalization import BatchNormalization
    
    def shallow_cnn(input_shape, num_classes):         # CNN 基本结构
    model = Sequential()
    model.add(Conv2D(32, (3, 3), padding='same',
                     input_shape=input_shape))
    model.add(Activation('relu'))
    model.add(Flatten())
    model.add(Dense(num_classes))
    model.add(Activation('softmax'))
    
    return model
    
    def leNet(input_shape, num_classes):             # 深度学习经典结构
    model = Sequential()
    
    model.add(Conv2D(20, (5, 5), padding='same', input_shape=input_shape))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    
    model.add(Conv2D(50, (5, 5), padding='same'))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 3)))
    
    model.add(Flatten())
    
    model.add(Dense(500))
    model.add(Activation('relu'))
    model.add(Dense(num_classes))
    model.add(Activation('softmax'))
    
    return model
    
    
    def mini_vgg(input_shape, num_classes):       # 全3x3卷积结构(vgg) + 多层堆叠Conv-relu + BN + Dropout
    model = Sequential()
    
    model.add(Conv2D(32, (3, 3), padding='same', input_shape=input_shape)) # 第一个卷积层需输入输入大小
    model.add(Activation('relu'))
    model.add(BatchNormalization(axis=-1))  #  axis:表示 features axis,即特征个数的维度
    
    model.add(Conv2D(32, (3, 3), padding='same'))
    model.add(Activation('relu'))
    model.add(BatchNormalization(axis=-1))
    
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))    # 在模型中(而不是尾端)进行小剂量dropout
    
    model.add(Flatten())
    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(BatchNormalization())    # 这里不再设置 axis=-1?
    model.add(Dropout(0.5))
    model.add(Dense(num_classes))
    model.add(Activation('softmax'))
    
    return model
    
    # 导入数据
    print('loadig images...')
    faces, emotions = load_ck()
    print('finished loading {} images'.format(faces.shape[0]))
        - 训练 + 可视化
 
    from sklearn.model_selection import train_test_split
    from keras.optimizers import SGD
    
    # 划分训练、测试集
    xtrain, xtest, ytrain, ytest = train_test_split(faces, emotions, 
                                                test_size=0.2, shuffle=True, random_state=42)
    print("train on {} images and validate on {} images".format(xtrain.shape[0], xtest.shape[0]))  
    
    input_shape = (224, 224, 3)
    num_classes = 7
    lr = 0.01
    batch_size= 32
    epochs = 50
    # decay = lr / epochs  # 设置学习率衰减:随着训练,慢慢减小学习率
    
    # 模型编译
    opt = SGD(lr=0.01)
    # opt = SGD(lr=0.01, decay=decay, momentum=0.9, nesterov=True)
    
    model = leNet(input_shape, num_classes)    # 选择模型
    # model = mini_vgg(input_shape, num_classes)
    model.compile(loss='categorical_crossentropy', 
                            optimizer=opt, metrics=['accuracy'])    
    # 训练网络
    history = model.fit(xtrain, ytrain, validation_data=(xtest, ytest),
          batch_size=batch_size, epochs=epochs, verbose=1) 
                              
    
    # 模型可视化
    import matplotlib.pyplot as plt
    
    def plot_training(history):
      # list all data in history
      print(history.history.keys())
      
      # Plot training & validation accuracy values
      plt.plot(history.history['acc'])
      plt.plot(history.history['val_acc'])
      plt.title('Model accuracy')
      plt.ylabel('Accuracy')
      plt.xlabel('Epoch')
      plt.legend(['Train', 'Test'], loc='lower right')
      plt.show() # 分两张图 or 使用一张图
    
      # Plot training & validation loss values
      plt.plot(history.history['loss'])
      plt.plot(history.history['val_loss'])
      plt.title('Model loss')
      plt.ylabel('Loss')
      plt.xlabel('Epoch')
      plt.legend(['Train', 'Test'], loc='upper right')
      plt.show()
    
    plot_training(history)                               
        结果


进行数据增强
在划分数据集之后,补上代码:
    #(进行数据增强)
    image_generator = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                                     height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                                     horizontal_flip=True, fill_mode="nearest")
        并替换掉拟合的过程:
    # (数据增强+训练)
    history =  model.fit_generator(image_generator.flow(xtrain, ytrain, batch_size=batch_size),
                               validation_data=(xtest, ytest), steps_per_epoch=len(xtrain) // batch_size,
                               epochs=epochs, verbose=1)
        - 结果
 
结果显示效果欠佳(可能由于正则化强度过高!另外模型的基础性较弱同样无法满足预期需求)


后更改参数(旋转30改为10),结果变好一点,从20%到了35%
高级优化算法
在之前的讨论中,在此基础上进一步提出了一种新的算法框架。此外,在实际应用中发现这一方法在提高模型训练效率方面表现出色。
- 在最优配置下能够实现比SGD更高的分类准确度。
 - 除了学习速率之外,在最优配置下还能够通过调节其他超参数使网络训练得更加稳定。
 - 在最优配置下减少获得理想准确率所需的时间(例如 epoch 数)。
 
since the learning rate of a network represents (1) an paramount hyperparameter requiring meticulous adjustment and (2) an inherently difficult and time-consuming parameter to calibrate accurately, deep learning experts have posited that it is plausible to adaptively adjust the learning rate (and in certain instances, even tailor it per-parameter basis) as training unfolds.
接下来关注 适应性学习率(adaptive learning rates)
适应性学习率方法
- SGD
 - Adagrad
 - Adadelta
 - RMSprop
 - RMSprop
 - Nadam
 
与其彻底考察每一种优化算法,并将其应用到数据集上并识别存在的问题,不如集中精力掌握两种或三种优化算法。
Typically, the outcomes in deep learning endeavors are achieved through the integration of optimization algorithms (and their associated parameters) and the researcher's proficiency in steering these algorithms.
所以,应该该学习掌握三种算法:
SGD, Adam, and RMSprop
深度学习的原料和菜谱
四种原料:
- 数据集
 - 损失函数
 - 神经网络模型
 - 优化算法
 
深度学习菜谱:(Andrew Ng)

(可以看出来还是根据 Loss 来判断!)
训练四部曲:
- Model Training
 - Model Training and Validation Process (which Andrew Ng refers to as “development”)
 - Model Validation
 - Model Testing
 
注解:数据集合 = 训练样本库 - 60%(基于 Training loss 的方差) + 验证样本库(基于 Training-validation 的方差) + 测试样本库
注解:数据集合 = 训练样本库 - 60%(基于 Training loss 的方差) + 验证样本库(基于 Training-validation 的方差) + 测试样本库
- 如果 training error 显著较大,
那么我们应该考虑通过加入更多层和神经元单元来增强当前模型结构,
我们还应该考虑延长训练时长(即投入更多 epoch),同时调整学习率(采用较低的学习率能够延长训练时长并有助于防止 overfitting)。 
与他人交流学习是一种有效的方式;通过加深思考深度 、延长学习时长以及更加专注地进行系统性研究等方法,可能会显著提升个人的学习效果。
如果 training/validation discrepancy 较大,则应考察 regularization parameter. 是否采用了 Dropout?是否运用了 data augmentation? 同时,请考虑补充更高品质的数据集或扩大训练样本量. 建议尝试采用不同的网络架构.
如果 training-validation error 很低,但是validation set error 很高,
为了更好地核查训练数据的质量,请问训练集中图片与验证集中图片是否存在相似性?如果存在这种情况,请花些时间补充完善训练集;此外,在确认无误后,请进一步关注模型的正则化程度——请问当前设置是否过激?最后建议重新评估并更换模型。
请问当前的testing error数值是否偏高?这表明模型可能已经出现了过拟合现象。为了进一步优化模型性能,请考虑收集更大规模的训练数据以缓解这一问题。

使用迁移学习还是从头训练?
由两个因素来判断:
- 数据集的大小
 - 数据集和预训练数据集的相似度大小
 

没种分类 理想来说应该有 1000-5000个样本。
2.2 更大数据集,更高级网络
两种新的图像预处理方法
均值减法预处理器
对于有RGB三通道的彩色图,采用以下的均值减法:
- R=R−μR
 - G=G−μG
 - B=B−μB
 
均值减法用于减少分类期间照明变化 的影响。

    12 def preprocess(self, image):
    13 # split the image into its respective Red, Green, and Blue
    14 # channels
    15 (B, G, R) = cv2.split(image.astype("float32"))
    16
    17 # subtract the means for each channel
    18 R -= self.rMean
    19 G -= self.gMean
    20 B -= self.bMean
    21
    22 # merge the channels back together and return the image
    23 return cv2.merge([B, G, R])
        Patch Preprocessing
该系统在训练过程中承担着从图像中提取M×N区域范围的任务。

左边:256x256 ;右边:从左边随机裁剪出的 227x227的区域
    # import the necessary packages
    from sklearn.feature_extraction.image import extract_patches_2d
    class PatchPreprocessor:
     def __init__(self, width, height):
          # store the target width and height of the image
          self.width = width
          self.height = height
    
    	  def preprocess(self, image):
          # extract a random crop from the image with the target width
          # and height
          return extract_patches_2d(image, (self.height, self.width),
               max_patches=1)[0]
        注: 使用 scikit-learn 中的 extract_patches_2d 函数 很容易完成。
裁剪预处理
在评估CNN模型时,我们对输入图像进行四周边缘及中间部分的裁剪,并对裁剪后的图像执行水平翻转操作。每个输入图像将产生10个样本数据。

这十个样本将采用卷积神经网络模型进行分析,并计算其平均概率。该上采样方法能够带来约1-2%分类准确率的提升,在具体效果可能更好的情况下。
    2 import numpy as np
    3 import cv2
    4
    5 class CropPreprocessor:
    6 def __init__(self, width, height, horiz=True, inter=cv2.INTER_AREA):
    7 # store the target image width, height, whether or not
    8 # horizontal flips should be included, along with the
    9 # interpolation method used when resizing
    10 self.width = width
    11 self.height = height
    12 self.horiz = horiz
    13 self.inter = inter
             def preprocess(self, image):
          # initialize the list of crops
          crops = []
          # grab the width and height of the image then use these
          # dimensions to define the corners of the image based
          (h, w) = image.shape[:2]
          coords = [
               [0, 0, self.width, self.height],
               [w - self.width, 0, w, self.height],
               [w - self.width, h - self.height, w, h],
               [0, h - self.height, self.width, h]]
          # compute the center crop of the image as well
          dW = int(0.5 * (w - self.width))
          dH = int(0.5 * (h - self.height))
          coords.append([dW, dH, w - dW, h - dH])
            # loop over the coordinates, extract each of the crops,
          # and resize each of them to a fixed size
          for (startX, startY, endX, endY) in coords:
               crop = image[startY:endY, startX:endX]
               crop = cv2.resize(crop, (self.width, self.height),
                    interpolation=self.inter)
               crops.append(crop)
            # check to see if the horizontal flips should be taken
    if self.horiz:
     # compute the horizontal mirror flips for each crop
     mirrors = [cv2.flip(c, 1) for c in crops]
     crops.extend(mirrors)
    # return the set of crops
    return np.array(crops)
        GoogLeNet
Inception 模块
mini_GoogLeNet
ResNet
Residual 模块
ResNet
3. ImageNet Bundle
3.1 在 ImageNet上进行训练
GPUs
准备 ImageNet
- 下载数据集
 - 认识数据集
 
在 ImageNet 上进行训练
3.2 深度学习的实际应用
情绪识别
总结
参数
附录







参考链接
