Notes: Kaggle Courses 4: Intro to DL & Computer Vision

本文最后更新于:2022年3月26日 凌晨

Intro to Deep Learning

A Single Neuron

The Linear Unit

下面是一个neuron(或称unit)的示意图,x是输入;w是x的权重weightbbias,是一种特殊的权重,没有和bias相关的输入数据,它可以独立于输入修改输出。神经网络通过修改权重来“learn”。

y是这个神经元输出的值,𝑦=𝑤𝑥+𝑏𝑦=𝑤𝑥+𝑏,刚好是一个直线的方程,w是斜率,b是在y轴上的截距。

The Linear Unit: 𝑦=𝑤𝑥+𝑏

Example - The Linear Unit as a Model

单个神经元是通常只会在更大的网络中发挥作用,单神经元模型是线性模型。当w=2.5,b=90w=2.5, b=90时,这个线性模型可以用来反应糖'sugars'和卡路里'calories'的关系:

Computing with the linear unit.

Multiple Inputs

对于多个输入,也是这样将每个输入乘以权重,并把它们相加。下面这个对应的公式为:y=w0x0+w1x1+w2x2+b.y=w_{0} x_{0}+w_{1} x_{1}+w_{2} x_{2}+b.
A linear unit with three inputs.

Linear Units in Keras

在Keras中创建模型最简单的方法是使用keras.Sequential,下面这个示例表示一个线性模型,可以输入3个特征(‘sugars’, ‘fiber’, ‘protein’),并且只有一个输出:‘calories’。

1
2
3
4
5
6
7
from tensorflow import keras
from tensorflow.keras import layers

# Create a network with 1 linear unit
model = keras.Sequential([
layers.Dense(units=1, input_shape=[3])
])

第一个参数units定义输出的个数,input_shape告诉Keras输入特征的数量。目前只需要用到input_shape=[num_columns],input_shape还可以支持使用更复杂的数据:[height, width, channels]

Tensors是TensorFlow版本的numpy数组,并且做了一些使它更适合用于机器学习的改变,Tensors与GPU/TPU加速器兼容,而TPU就是专为Tensors而设计的。在Keras内部,使用Tensors表示神经网络的权重。

model.weights可以用来查看权重,在训练开始前,权重都会被初始化为随机值。

Deep Neural Networks

Layers

神经网络会将神经元组成层(layers),合并有相同的输入的线性神经元,就得到了一个稠密层(dense layer)
A dense layer of two linear units receiving two inputs and a bias.

The Activation Function

两个中间没有其他东西的稠密层,效果并不会比一个稠密层的效果好多少,“稠密层本身不能带我们离开线和面的世界”,我们需要的是非线性(nonlinear),需要激活函数(activation function)。

没有激活函数,模型只能学习线性关系,为了拟合曲线,需要使用激活函数

激活函数就是应用于每一层输出的函数,最常见的是rectifier函数max(0,x).max(0,x).

把rectifier应用到一个线性单元上时,就得到了rectified linear unit,或简称ReLU。这样这个线性单元的输出就是max(0,wx+b)max(0,w\cdot x+b)

Stacking Dense Layers

堆叠层来获得复杂的数据转换:
A stack of dense layers makes a "fully-connected" network.

输出层之前的层有时被称为隐藏层(hidden),因为我们没有直接看到它们的输出。上图在输出之前使用了一个线性单元,而不是激活函数,这样做使这个模型适用于回归任务,在分类任务中,可能要在这里使用激活函数。

Building Sequential Models

我们将用Sequential模型来连接一系列的层,建立上图的模型,第一次获得输入,最后一层产生输出

1
2
3
4
5
6
7
8
9
10
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
# ReLU 隐藏层
layers.Dense(units=4, activation='relu', input_shape=[2]), # 输入
layers.Dense(units=3, activation='relu'),
# 线性输出层
layers.Dense(units=1),
])

一定要把所有的图层放在一个列表中,比如[layer,layer,layer,...]。要添加激活函数层,只要设置activation参数即可,比如ReLU:activation='relu'

Stochastic Gradient Descent

Introduction

前面两节讲了如何构建全连接的网络(fully-connected networks),新创建的网络中的权重都是随机的,这一节就开始介绍如何训练神经网络。

训练模型中每条数据需要输入一些特征(features)和一个期望的输出目标(target),训练的过程会调整权重,使网络可以通过输入的特征计算出期望的目标。

除了训练数据,还需要:

  • “损失函数”,用来衡量网络预测结果的好坏。
  • “优化器”,可以告诉网络如何改变其权重。

The Loss Function

损失函数(loss function)测量target真实值和模型预测值之间的差异。不同的问题需要使用不同的损失函数,比如回归问题(regression problems)常用的损失函数就是平均绝对误差MAE(mean absolute error),MAE通过差的绝对值abs(y_true-y_pred)测量预测值y_pred与真实目标y_true的差异。
数据集上的总MAE,是所有这些差的绝对值的平均值。

平均绝对误差是拟合曲线和数据点之间的平均长度

除了MAE之外,回归问题还有其他的损失函数:均方误差(mean-squared error,MSE)或Huber损失(Huber loss),它们都可以在Keras中使用。
在训练期间,模型将使用损失函数作为指导,以找到正确的权重值(loss越小越好)。换句话说,损失函数告诉网络它的目标。

The Optimizer - Stochastic Gradient Descent

随机梯度下降,这里的“随机”用的是stochastic,而非熟悉的random,查了一下维基百科:

Although stochasticity and randomness are distinct in that the former refers to a modeling approach and the latter refers to phenomena themselves, these two terms are often used synonymously. Furthermore, in probability theory, the formal concept of a stochastic process is also referred to as a random process.

stochastic 偏向指建模方法,random 偏向指现象本身,很多时候这两个词是同义的。

优化器(optimizer)是一种调整权重来使loss最小化的算法。深度学习中使用的所有优化器算法都属于一个叫做随机梯度下降的家族,训练网络的过程就是一次次迭代下面的算法:

  1. 采集一些训练数据,通过网络进行预测
  2. 测量预测值和真实值之间的损失
  3. 最后,调整权重使loss更小

使用随机梯度下降的神经网络

每个迭代的训练数据样本称为一个minibatch(或称batch),而一轮完整的训练数据称为一个epoch。你训练的次数是网络看到每个训练示例的次数。网络看到每个训练示例的次数,就是训练的轮数。

上面的动画显示了线性模型在使用SGD进行训练,淡红色的点是整个数据集,变化的实心红点表示minibatch,每次SGD看到一个新的minibatch,它都会将权重(w斜率,by轴截距)移向batch的正确值,经过一轮又一轮的batch,直线最终会收敛到最佳状态,可以看到,权重越接近真实值,loss就越小。

Learning Rate and Batch Size

可以注意到直线每次会在batch的方向上发生一个小的移动,这个移动变化的大小取决于学习率(learning rate),学习率越小,在逼近正确值的过程就越长。

学习率(Learning Rate)和batch的大小(Batch Size)是对SGD训练进度影响最大的两个参数。它们之间的相互作用往往很微妙,对这些参数的正确选择并不总是显而易见的。(我们将在练习中探讨这些影响)

幸运的是,对于大多数工作来说,没有必要进行广泛的超参数搜索以获得满意的结果。Adam是一种SGD算法,具有自适应学习率,使其适用于大多数问题,而无需任何参数调整(从某种意义上说,它是“自调整”)。Adam是一个伟大的通用优化器。

Adding the Loss and Optimizer

定义模型后,可以使用模型的compile方法添加损失函数和优化器:

1
2
3
4
model.compile(
optimizer="adam",
loss="mae",
)

只需要一个字符串就可以指定loss和optimizer;也可以通过Keras API直接访问这些参数——例如想要优化参数——但对我们来说,默认值就可以正常工作。

梯度(gradient)是一个向量,告诉我们权重应该朝哪个方向移动,也就是说它告诉我们如何改变重量使损失变化最快,我们称过程为梯度下降(descent),是因为它使用梯度将损失曲线下降到最小值,随机(stochastic)是指每次选取的minibatches是从训练数据中随机选取的随机样例。SGD即Stochastic Gradient Descent。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])

# 给模型设置优化器和损失函数
model.compile(
optimizer='adam',
loss='mae',
)

# 每轮给模型 256 行数据,这样训练 10 轮
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=10,
)

在每一轮的训练后,都会输出当前的loss,并且训练过程中的loss都会被保存起来,所以我们可以用它们作图来更直观的看出loss的变化:

1
2
3
4
5
6
import pandas as pd

# convert the training history to a dataframe
history_df = pd.DataFrame(history.history)
# use Pandas native plot method
history_df['loss'].plot();

loss的变化曲线逐渐变得趋于水平了,就说明模型已经学会了它能学会的一切,所以没有必要让它进行更多的迭代,如果想要优化loss,更应该做的是调整模型。

Overfitting and Underfitting

Interpreting the Learning Curves

模型得到的数据是由信息(signal)和噪声(noise)组成的,我们希望它从signal中学习到模式,这样可以使其在预测过程中表现良好,噪声是只在训练数据中正确的案例。

下图绘制了在训练数据上和在测试数据上的loss情况,这些曲线我们称之为学习曲线(learning curves),为了有效的训练深度学习模型,我们需要能够解释它们。

The validation loss gives an estimate of the expected error on unseen data.

在模型学习signal和noise的过程中,训练loss会逐渐下降,但只有模型学习到signal时,验证loss才会降低。在学习signal的过程中,两条曲线都会下降,但是如果模型学习了noise,那么两条曲线之间就会出现空隙(gap),这个空隙的大小,可以反应模型学到了多少noise。理想情况下我们希望模型只学习signal不学习noise,但这几乎是不可能的,只能以学习到很多的noise为代价,让模型尽可能多的学习到signal,从上图可以看出,当出现了某一点后,验证loss会逐渐上升。

Underfitting and overfitting.

在训练模型时可能会出现两个问题:

  1. signal不足或噪声过大。未充分拟合训练集 是指由于模型没有学习到足够的signal,导致loss没有尽可能低。
  2. 过度拟合训练集是指由于模型学习了太多的噪声,导致loss没有尽可能低。

训练深度学习模型的诀窍是在两者之间找到最佳平衡,我们将研究几种从训练数据中获取更多signal的方法,同时减少噪声。

Capacity

模型的容量是指它能够学习的模式的大小和复杂性,对于神经网络来说,这在很大程度上取决于它有多少神经元以及它们如何连接在一起。如果网络似乎不适合数据,应该尝试增加其容量。

可以通过使网络更宽(将更多神经元添加到现有层)或使其更深(添加更多层)来增加网络的容量。更宽的网络更容易学习更多的线性关系,而更深的网络更喜欢非线性关系。哪个更好取决于数据集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
model = keras.Sequential([
layers.Dense(16, activation='relu'),
layers.Dense(1),
])

wider = keras.Sequential([
layers.Dense(32, activation='relu'),
layers.Dense(1),
])

deeper = keras.Sequential([
layers.Dense(16, activation='relu'),
layers.Dense(16, activation='relu'),
layers.Dense(1),
])

Early Stopping

当模型学习到很多噪声时,验证损失可能会在训练期间开始增加,为了防止这种情况,只要验证loss似乎不再减少,我们就可以停止训练。以这种方式中断训练被称为提前停止(Early Stopping)。

让模型在验证loss最小的位置停止

一旦检测到了验证loss再次上升,就可以将权重重置回之前loss最小值的位置,确保了模型不会继续过拟合。

Adding Early Stopping

在Keras中,我们通过回调(callback)在训练中提前停止,回调函数是一个在网络运行时需要经常运行的函数。提前停止回调将在每个epoch之后运行。(Keras预先定义了各种有用的回调,也可以定义自己的回调。)

1
2
3
4
5
6
7
from tensorflow.keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(
min_delta=0.001, # minimium amount of change to count as an improvement
patience=20, # 20个
restore_best_weights=True,
)

如果在过去20个epochs中,验证loss没有改善0.001,那么停止训练,保留找到的最佳模型。

Example - Train a Model with Early Stopping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from tensorflow import keras
from tensorflow.keras import layers, callbacks

early_stopping = callbacks.EarlyStopping(
min_delta=0.001, # minimium amount of change to count as an improvement
patience=20, # how many epochs to wait before stopping
restore_best_weights=True,
)

model = keras.Sequential([
layers.Dense(512, activation='relu', input_shape=[11]),
layers.Dense(512, activation='relu'),
layers.Dense(512, activation='relu'),
layers.Dense(1),
])
model.compile(
optimizer='adam',
loss='mae',
)
history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=500,
callbacks=[early_stopping], # put your callbacks in a list
verbose=0, # turn off training log
)

history_df = pd.DataFrame(history.history)
history_df.loc[:, ['loss', 'val_loss']].plot();
print("Minimum validation loss: {}".format(history_df['val_loss'].min()))

Dropout and Batch Normalization

keras有几十中layers,可以在 Keras docs 中查看示例。这一节将介绍两种特殊的layer,它们本身不包含神经元。

Dropout

Dropout可以帮助修正overfitting。

前面讲过了数据的欠拟合和过拟合,出现了过拟合的情况,是因为网络模型学习了训练数据中的虚假的模式(噪声的模式),模型为了学习到这个模式 通常会依赖非常特定的权重组合,所以这种权重的组合才实现出的模式,往往是很脆弱的,移除一个就会瓦解。

Dropout就是在训练的每一步都随机删除一层输入单元的一小部分,这使得网络更难从训练数据中学习这些虚假模式。相反,它必须寻找广泛的、一般的模式,其权重模式往往更稳健。

Here, 50% dropout has been added between the two hidden layers.

Adding Dropout

在Keras中,dropout率参数rate定义了要关闭的输入单元的百分比。将Dropout layer放在要应用Dropout的层之前:

1
2
3
4
5
6
keras.Sequential([
# ...
layers.Dropout(rate=0.3), # apply 30% dropout to the next layer
layers.Dense(16),
# ...
])

Batch Normalization

"batch normalization"或称"batchnorm"这个特殊层有助于纠正缓慢或不稳定的训练。在神经网络中通常需要将所有的数据放在一个通用的尺度上,可以使用scikit learn的StandardScaler或MinMaxScaler之类的工具,这是因为SGD根据数据产生的激活量按比例改变网络中的权重,训练中的数值大小范围不一样可能会导致不稳定的训练。

可以在数据进入网络之前对其进行规范化(normalize),但是更好的操作是在网络的内部对数据进行规范化,batch normalization layer就是用来对网络中的数据进行规范化操作的,它会用其自身的平均值标准差对batch进行标准化,然后用两个可训练的重缩放参数(trainable rescaling parameters)将数据放在一个新的尺度上。

使用batchnorm的模型往往需要较少的时间完成训练,也可以解决可能导致训练“停滞”的各种问题,所以可以考虑在模型中添加batchnorm。

Adding Batch Normalization

batchnorm可以放在相对其他层的各种位置上,如果用它作为网络的第一层,就起到了一个代替预处理时对数据进行标准化的操作,类似Sci-Kit Learn的 StandardScaler,也可以放在某一层之后:

1
2
layers.Dense(16, activation='relu'),
layers.BatchNormalization(),

或在某层和它的激活函数之间:

1
2
3
layers.Dense(16),
layers.BatchNormalization(),
layers.Activation('relu'),

Example - Using Dropout and Batch Normalization

如果模型中使用了dropout,就应该在层中添加更多的单元,因为每次都会被随机抛弃一部分不参与的单元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
layers.Dense(1024, activation='relu', input_shape=[11]),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1024, activation='relu'),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1024, activation='relu'),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(1),
])

model.compile(
optimizer='adam',
loss='mae',
)

history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=256,
epochs=100,
verbose=0,
)

# Show the learning curves
history_df = pd.DataFrame(history.history)
history_df.loc[:, ['loss', 'val_loss']].plot();

Binary Classification

Introduction

之前的部分在介绍用深度学习解决回归问题,这一节介绍用深度学习解决分类问题。

Binary Classification

二分类问题是指分成两类的问题,比如用"Yes"/"No"来回答的问题。我们需要给数据class label:0 或 1,数字标签是神经网络模型可以使用的数据形式。

Accuracy and Cross-Entropy

准确性(Accuracy)是衡量分类问题成功与否的众多指标之一。accuracy是正确预测与总预测的比率:accuracy = number_correct / total。一个总是正确预测的模型的准确度得分为1.0。在所有其他条件相同的情况下,每当数据集中的类以大约相同的频率出现时,准确度是一个合理的指标。

accuracy(以及大多数其他分类指标)的问题在于,它不能用作损失函数。SGD需要一个平稳变化的损失函数,但精度,作为计数的比率,在“跳跃”中变化。因此,我们必须选择一个替代品作为损失函数。这个替代品是交叉熵函数(cross-entropy function)。

现在,回想一下损失函数定义了训练期间网络的目标。通过回归,我们的目标是最小化预期结果和预测结果之间的距离。我们选择了MAE来测量这个距离。

对于分类,我们想要的是概率之间的距离,这就是交叉熵提供的。Cross-entropy是一种度量从一个概率分布到另一个概率分布的距离的方法。

Cross-entropy penalizes incorrect probability predictions.

我们希望我们的网络以1.0的概率预测正确的班级。预测概率离1.0越远,交叉熵损失越大。

我们使用交叉熵的技术原因有点微妙,但从这一节中我们要了解的主要内容是:使用交叉熵来进行分类损失;你可能关心的其他指标(如准确性)也会随之提高。

Making Probabilities with the Sigmoid Function

交叉熵和精度函数都需要概率作为输入,即0到1之间的数字。为了将密集层产生的实值输出转化为概率,我们附加了一种新的激活函数,即sigmoid激活函数。

The sigmoid function maps real numbers into the interval [0,1].

为了得到最终的类预测,我们定义了一个阈值概率。通常这将是0.5,因此四舍五入将为我们提供正确的类别:低于0.5表示标签为0的类别,0.5或以上表示标签为1的类别。0.5阈值是Keras默认使用的精度指标。

Example - Binary Classification

除了最后一层用了“sigmoid”激活,它用来产生类概率,其他部分和回归任务一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
layers.Dense(4, activation='relu', input_shape=[33]),
layers.Dense(4, activation='relu'),
layers.Dense(1, activation='sigmoid'),
])

model.compile(
optimizer='adam', # Adam也适用于分类问题
loss='binary_crossentropy', # 损失函数为 交叉熵函数
metrics=['binary_accuracy'],
)

# 提前停止 回调函数
early_stopping = keras.callbacks.EarlyStopping(
patience=10,
min_delta=0.001,
restore_best_weights=True,
)

history = model.fit(
X_train, y_train,
validation_data=(X_valid, y_valid),
batch_size=512,
epochs=1000,
callbacks=[early_stopping],
verbose=0, # hide the output because we have so many epochs
)

history_df = pd.DataFrame(history.history)
# Start the plot at epoch 5
history_df.loc[5:, ['loss', 'val_loss']].plot()
history_df.loc[5:, ['binary_accuracy', 'val_binary_accuracy']].plot()

print(("Best Validation Loss: {:0.4f}" +\
"\nBest Validation Accuracy: {:0.4f}")\
.format(history_df['val_loss'].min(),
history_df['val_binary_accuracy'].max()))

1
2
Best Validation Loss: 0.5482
Best Validation Accuracy: 0.7619

Detecting the Higgs Boson With TPUs

这是属于Intro to Deep Learning的一节Bonus Lesson,介绍如何使用TPU的。

1
2
3
4
5
6
7
8
9
10
11
# TensorFlow
import tensorflow as tf
print("Tensorflow version " + tf.__version__)

# Detect and init the TPU
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.get_strategy() # default strategy that works on CPU and single GPU
print("Number of accelerators: ", strategy.num_replicas_in_sync)

Computer Vision


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!