经验拾忆纯手工-DeepLearningMetricsNg

42次阅读

共计 7432 个字符,预计需要花费 19 分钟才能阅读完成。

前言

看 Andrew Ng 视频,总结的学习心得。
虽然本篇文章可能不是那么细致入微,甚至可能有了解偏差。
但是,我喜欢用更直白的方式去理解知识。

数据划分

传统机器学习数据的划分

传统机器学习一般都是小规模数据(几万条)

那么可以  训练集:验证集:测试集 = 6:2:2

若是大规模深度学习一般都是大规模数据(几百万条)

训练集: 验证机:测试集 = 9:0.5:0.5

划分 验证集 可以过早的为我们预测 指标和精度

偏差 与 方差

高偏差:训练集和测试集 loss 都比较高(比人预测的 loss 高很多)(欠拟合)
高方差:训练集 Loss 低,测试集 Loss 高。所以训练集和测试集 loss 相差太大,也成为(过拟合)

防止过拟合的几种方法

损失函数 (惩罚项系数) 正则化(regularization)

可分两种(L1 正则化惩罚 和 L2 正则化惩罚)下面只以 L2 为例,L2 用的也是比较多的)
正则化系数公式:

loss = ...
new_loss = loss + (λ/2m) * w^2
w = w - learning_rate * 梯度

上面公式的单调变换解释:

求梯度的时候 λ 越大,new_loss 越大,求得的梯度越大(正比)w 减去的值就越大。w 变得就越小。w 越小,一定程度上特征就衰减了许多。就有效的放置了过拟合哦

对应 API(有两种方式):

L1 = keras.regularizers.l2(0.01)   # TF2 当作 keras 的 Layers 定义来使用
L1 = tf.nn.l2_loss(w_b)            # 看到里面传递 w 和 b 了吧,这种是偏手动方式实现的 API

如果你想使用手撕实现,下面有个例子(伪代码):

for
    with tf.GradientTape() as tape:
        ...
        loss_reg = [tf.nn.l2_loss(w_b) for w_b in model.trainable_variables] # [w1,b1,w2,b2]
        print(tf.reduce_sum(loss_reg))        # tf.Tensor(2.98585, shape=(), dtype=float32) # 就是这个形状
        loss = loss + loss_reg

另一种正则化方式(regularization)— DropOut

"随机" 剪枝,略,TF 框架一步到位

还有一种防止过拟合的方式(数据增强)

防止过拟合的另一种方式,就是需要的大量的数据来支撑,那么数据就那么点,怎么办?

数据增强(其原理就是增大样本数量,小幅度翻转等)某种程度上,就是增加了样本。

最后一种防止过拟合的方法(earlystopping)

earlystopping(tf.keras.callback 模块中,有这个 callback 函数,注释部分有解释)

callbacks = [keras.callbacks.TensorBoard(logdir),
    keras.callbacks.ModelCheckpoint(output_model_file, save_best_only=True),
    # 在这里
    keras.callbacks.EarlyStopping(patience=5, min_delta=1e-3)
    # 验证集,每次都会提升,如果提升不动了,小于这个 min_delta 阈值,则会耐性等待 5 次。# 5 次过后,要是还提升这么点。就提前结束。]

数据预处理

标准化 和 归一化能起到什么作用:

做了标准化 和 归一化 可以 让函数收敛的 更快速(梯度下降的快)参考(圆形下降的快,和 椭圆下降的慢)其次,在神经网络中,BN 层还有额外的效果。每层网络的不同参数,可能会导致 "数据分布" 散乱、变形
因此,BN 可以有效 防止数据分布变形。(其实说白了也是 "加速函数收敛" , 加速 NN 训练)

注意点

训练集 和 测试集的样本特征("要么都做处理,要么都不做处理")(就是相同待遇的意思。。。)专业点叫做:"保证训练集 和 测试集 相同分布"

数据 随机分布有什么影响(Andrew ng 解释)?

假如训练集的数据分布 训练的目标是正中靶心。
而预测时,假如你的预测集数据分布 和 训练集数据分布的不同。
那么很可能会 预测集 预测的时候(是另一个靶心)。
所以你训练的再好,到预测集的靶子上 也是脱靶。。。
所以 训练集 和 测试集 的 相同分布很重要

数据预处理大体分 2 类:

1. 中心化处理
2. 缩放处理

zero-centered (中心化处理)

平移 — 减去固定值

scale (缩放处理)

归一化:除以 最大值 - 最小值
标准化:除以 标准差

下面的操作都是通过 中心化处理 + 缩放处理 联合组成的

normalization(归一化)sklearn 那里也提到过

目标:

将数据收敛到 [0,1]

公式

x - min(x)          # 中心化
------------------
max(x) - min(x)            # 缩放处理

StandardScaler(标准化)

目标:

将数据转化为标准 正态分布(均值为 0,方差为 1)

公式:

x - 平均值
-----------------
    标准差

标准化 和 归一化 选哪个???

视觉图片:归一化
其他:标准化较好

数据集不足怎么办?

我自己也遇到过这样的问题。
我之前做一个 QA 聊天机器人时。数据是百度知道的爬的。
但是(用过的应该清楚。百度知道有很多用户的垃圾回答,是用户刷分的。)
问了解决这一问题。我的解决思路是:通过 Pandas, 筛选答案长度至最小。
但是这样。就可能筛选除了 大量大量的 原生数据
再加上,把(原数据中 “ 有问无答 ” 的问答对)过滤扔掉。
那么弄下来的源数据,几乎没剩多少了。。(我记得我当时弄了 400W+ 问答对)
筛选到最后(问答长度 15 个汉字,筛选掉空回答)(只剩下 几万条了。。。)
后来,我急中生智。在网上找了一些 中文语料库(我用的青云中文语料库)
把它融合到 我自己的 语料库中。。。

但是训练后的结果,全是人家 青云语料库的 问答内容。。。
后来也没去继续深究了。。。

后来正好看到 Ng。提到这一问题,记录一下相应的应对措施!

训练集:青云语料 + 1/2 自己的语料
测试集: 1/4 自己的语料
验证集:1/4 自己的语料

随机初始化权重

随机初始化认知

为什么不是初始化为 0???

因为神经网络中,W 初始化为 0 的话,会导致反向传播后,所有神经元会训练同一个网络。一点效果没有

为什么不初始化很大的值或者很小的值???

这是分情况来定的。比如你用的 tanh 或者 sigmoid 函数
由脑海中的图像可知(求导 或 斜率),当 初始值过大,或者过小。都会可能导致,y 直接落在 sigmoid 的  顶部和底部(就是斜率水平,近乎为 0)落在了水平的梯度。这样的梯度,猴年马月也降不下去啊。。。。。如果落在了 倾斜陡峭的梯度。那么梯度下降的一定很快啦。

如果做了 BatchNormalization,那么可使用 高斯 x 0.01

正态分布 * 拉低值

np.random.randn(2,2)  * 0.01            # 2,2 是形状,这个 0.01 可以自己调节。总之,小一点 最好,但不要太小

如果使用了 Relu 激活函数,对应 初始化方法

np.random.randn(shapex, shapey) *  np.sqrt(2/shapex)    # 系数为 2 

如果使用了 Tanh 激活函数,对应 初始化方法(NG 推荐,也叫 Xavier)

np.random.randn(shapex, shapey) *  np.sqrt(1/shapex) # 系数为 1

激活函数

激活函数认知

学习 Andrew Ng 课更深刻了解了激活函数。

神经网络中,为什么我们需要激活函数,甚至需要非线性激活函数?

首先挑明,我们使用神经网络的目的,就是想训练出更多,更丰富的特征。所以。一直用线性激活函数,或者不用激活函数。会使得你整个网络训练到头,还是线性的。就没意思了。它学不到丰富的特征的。因为神经网络多层是需要拿前一层的结果作为下一层的 x,所以有了如下公式:w3 (w2 (w1x+b) +b) +b
展开后,w3 * w2 * w1 * x + ......   
    很明显它依然是线性的。所以,无论你用多少层 神经网络。到最后它依然是线性的。。。。这样倒不如 一层网络也不用。直接上个 逻辑回归模型,效果估计也是一样的。。。。。。

当然有一些场合也需要使用 线性激活函数,比如 房价预测。身高预测。(这些都是线性回归模型)

这些情况,就可以使用 线性激活函数了。但是不妨想一想,就像上面 身高预测这些。是线性回归,并且 y 预测都是正数值。某种程度上,其实我们也可以使用 relu 激活函数,(因为 relu 的右半侧(就是大于 0 的部分)也是线性的哦)

我们 NN 隐层就大多数都使用非线性激活函数。

隐层:relu 或者 leakly relu 或者 tanh
输出层:sigmoid  或者 softmax 或者 tanh  等等

sigmoid

公式

   1  
---------
1 + e**(-x)

每个 out: (0, 1)  

二分类 out 之和为 1

对应 API:

1. tf.sigmoid(y)
2. 或函数参数 xxxxx (activations='sigmoid')
3. tf.keras.activations.sigmoid()

softmax


  e**x
---------------------------------
e**(x1) + e**(x2) + ... + e**(xn)

每个 out: (0,1)

多分类 out 之和为 1

对应 API:

1. tf.nn.softmax()
2. 函数参数 xxxxx (activations='softmax')
3. tf.keras.activations.softmax()

softmax 特点:

输出的是什么形状的张量,输出的就是什么形状的张量
也是有线性决策边界(线性 多 分类器)

tanh

coshx

e**x - e**(-x)
-------------
      2

sinhx

e**x + e**(-x)
--------------
      2

tanhx

 e**x - e**(-x)
-------------
e**x + e**(-x)
      
每个 out: (0,1) * 2 -1  ===>  (-1,1)

LSTM

对应 API:

1. tf.tanh(y)
2. 函数参数 xxxxx (activations='tanh')
3. tf.keras.activations.tanh()

relu

公式:

y = 0 if x < 0 else x    # 大于 0,梯度为 1 

对应 API

1. tf.nn.relu()
2. 或函数参数 xxxxx (activations='relu')
3. tf.keras.activations.relu()

leaky_relu: (小扩展)
    y = kx if x < 0 else x    
    tf.nn.leaky_relu()

损失函数

MSE(均方误差)

公式

Σ((y-y_predict)**2 )
--------------------
     n

对应 API

公式实现:tf.reduce_mean(tf.square( y-y_predict) )
tf.API:   
    tf.reduce_mean(tf.loss.MSE(y, y_predict) )

CrossEntropy(交叉熵)

熵公式:-Σ(plogp)
交叉熵公式:-(Σplogq)p 为真实值 One-hot, q 为预测值

p: [1,0,0]
q: [0.9, 0,0.1]
H = -(1*log0.9 + 0*log0 + 0*log0.1) = -log0.9 = -ln0.9 ≈ 0.1053....
tf 的 tf.math.log 相当于 ln

交叉熵 API:

交叉熵越小(y 与 y -predict 差距越小,预测较准确)交叉熵越大(y 与 y_predict 差距越大,交叉相乘累加后值大,说明预测错位了。。。所以交叉起来变大了)tf.API:(方式 1:直接是函数调用)loss = tf.losses.categorical_crossentropy([1,0,0], [0.9, 0, 0.1],from_logits=True)  # 第一个参数 y, 第二个参数 y_predict
    loss = tf.reduce_mean(loss)

tf.API:(方式 2:用类的 call 调用 , 这次以 二分类交叉熵为例)loss = tf.losses.BinaryCrossentropy(from_logits=True)([1], [0.1] )  # 结果为 2.+。因为 真实值是 1 类,而预测值概率是 0.1 太小了。所以肯定预测错了。loss = tf.reduce_mean(loss)
    
说明:categorical_crossentropy( ) # 第一个参数必须 one_hot,(第二个参数按理来说需要做 softmax,但是你传了 from_logigs=True,就不必 softmax 了)

梯度

SGD(Stochastic Gradent Descent):

解释 各种梯度下降的区别:
Mini-Batch Gradent Descent:

指定每次 mini-batch 个 来做梯度下降(就是每次指定多少个样本 来做 GD 的意思)这种介于  1- 全部样本之间的。是最优的

Batch gradent descent:

mini-batch 为全部样本

Stochastic gradent descent:

mini-batch 为 1 个样本
缺点:每次 1 个样本做 SGD,那么就失去了 向量化(矩阵乘代替循环)的 加速快感。。。。。

减去梯度,代表朝着梯度方向走

 w 新 = w 当前 - learning_rate * 梯度    

使用方式:

model.compile(..... ,optimizer=keras.optimizers.SGD(learning_rate=0.01))

再记录其他优化器之前,先补一个 指数加权平均 的知识

公式:

y = β * X 之前 +(1-β)* X 当前

图形曲线表现:

 β 越小:(小到 0.5):曲线越抖动频繁(锯齿 越厉害)(0.5 左右已经,严重上下跳动了)β 越大:(大至 1.0):曲线越光滑(无锯齿)所以 β:越大越好(涉及到一个技术 -- 偏差修正,如果你不修正。可能训练会稍微慢一些。无伤大雅)

Momentum(动量)

公式大概:

dw' =  β * dw-1 + (1-β) * dw        # 用 dw-1 掰弯 dw
db' =  β * db-1 + (1-β) * db        # 用 db-1 掰弯 db

公式理解:

在原来的梯度基础上,用 上一次的梯度方向,把当前将要计算的梯度掰弯

RMSProp

model.compile(..... ,optimizer=keras.optimizers.RMSprop(learning_rate=0.01, momentum=0.9))

Adam(强烈推荐)

TF-API: 默认原参数

model.compile(..... ,optimizer=keras.optimizers.Adam(
        learning_rate=0.001,
        beta_1=0.9,           # 学习率衰减参数
        beta_2=0.999,
        epsilon=1e-7,
    ),
)

其实这个 API 参数,我们只稍微调整一下 learning _ rate 即可,其他不用怎么。

学习率衰减

其实大多数 优化器内都有 学习率衰减参数,例如:

SGD(decay)
Adam(beta_1)

当然你也可以自己实现(按照样本已训练的批次,动态衰减)

learning rate = learning rate * 1/(epoch 轮数 * 衰减率 + 1)其实还有更多 可调节参数,就像 Adam 中的 那么多参数似。当然我压根也没想自己实现衰减。。可知 decay 越小,学习率衰减的越慢,当衰减率为 0 时。学习率压根就不衰减
而 decay 越大,学习率衰减的越快,当衰减率为 1 时。那衰减的就太夸张了~~

迁移学习(我想到一个词:移花接木)

应用场景

假如已经有现成的 狗类 识别的 神经网络模型
那么假如你现在想要 做一个 猫类的 识别

你完全可以把 狗识别 网络模型拿过来
然后把最后 输出层 扔掉,自己加一个新输出层(当然中间你也可以加一些新的 NN 层)
然后 旁敲侧击,只在最后一层提供输入,只对 新的输出层(或者你额外加的 NN)层训练。

应用条件

当你迁移后的数据有特别特别多的时候,那么你完全可以把 搬过来的 模型参数 从头到尾训练一遍。

就像你 狗模型,前面的网络学到了很多  毛,特征。(这猫也有嘛,所以正好可以用得上)然后你 在狗模型的基础上,训练猫模型(我不太了解猫~~~,比如说可以新学到猫的胡须之类的新特征)总结来说:新模型 = NN 层(狗)参数 + NN 层(猫)参数 + 输出层(猫)参数

当然,如果你迁移支持的数据,只有很少,根本不够强大的神经网络训练

那么,你就可以直接把,搬过来的模型参数固定住,直接只在 最后输出层,提供输入,进行训练
总结来说:新模型 = NN 层(狗)参数 + 输出层(猫)参数

迁移学习的主要目的思想:

当你 有很少的小数据集 A,但是你想训练成一个 NN 来 达到目的。可想而知,少量数据集 A 还不够 NN 塞牙缝的。。。所以,你需要找一些其他类似的数据集 B(量多的,好收集的)然后这些大量数据集 B,足以 驰骋于 NN,得到一个模型。(并且带着 训练好的参数)数据集 A 说:"
    大哥,你训练好的网络借我用用呗。你用了那么多数据,训练出的特征一定有我想要的。我把整个模型拿过来,只改一下最后一层的输入。然后只训练最后一层的参数。其他层的参数都用你的。"。数据集 B 大哥说:"可以"

迁移学习 API(Tensorflow2.0)

温馨提示:TF20 的 Keras Layers 是可以 用切片语法 选取具体网络层的,举个例子:

# from tensorflow import keras
# cut_resnet = keras.applications.DenseNet121(  # 使用现有 ResNet 模型
#     include_top=False,  # 不要最后一层,而是使用我们自己定义的全连接层
#     pooling='avg',
#     weights='imagenet',  # 初始化权重(从 imagenet 训练好模型参数来初始化)# )
# for layer in cut_resnet.layers[0:-3]:  # 部分可训练(fine-tune 分割)#     trainable=False             # 0 到 倒数第三层,参数不可训练
# 
# new_model = keras.models.Sequential()
# new_model.add(cut_resnet)
# new_model.add(其他层)

迁移学习 适用场景

  1. 统一使用领域(要么文本迁移要文本,要门图像迁移到图像。)
  2. 假如 A 迁移到 B(那么 A 的样本最好远大于 B 的样本)
  3. 假如 A 迁移到 B(最好 A 的许多特征信息,B 正好可以用得到。比如 猫狗,都有毛发,胡须,四条腿)

多任务学习(了解,用的少)

直接感观:我认为就像(类的继承,或者封装为一个函数,这样的概念。。)

你想训练 预测 各种各样类别的图片。你可以首先 用一个任务 训练一下 共有特征 的 NN。然后其他任务 用这个 训练好的 共有的特征的 NN。Ng 提示:你需要有庞大的神经网络支撑,不然效果不好。

正文完
 0