前言
看 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(其他层)
迁移学习 适用场景
- 统一使用领域(要么文本迁移要文本,要门图像迁移到图像。)
- 假如 A 迁移到 B(那么 A 的样本最好远大于 B 的样本)
- 假如 A 迁移到 B(最好 A 的许多特征信息,B 正好可以用得到。比如 猫狗,都有毛发,胡须,四条腿)
多任务学习(了解,用的少)
直接感观:我认为就像(类的继承,或者封装为一个函数,这样的概念。。)
你想训练 预测 各种各样类别的图片。你可以首先 用一个任务 训练一下 共有特征 的 NN。然后其他任务 用这个 训练好的 共有的特征的 NN。Ng 提示:你需要有庞大的神经网络支撑,不然效果不好。