经验拾忆(纯手工)=> DeepLearning+Metrics(Ng)

前言

看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提示: 你需要有庞大的神经网络支撑,不然效果不好。    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章