一、函数
tf.where(条件语句,真返回A, 假返回B)
条件语句真返回A,条件语句假返回B
a = tf.constant([1, 2, 3, 1, 1])
b = tf.constant([0, 1, 3, 4, 5])
c = tf.where(tf.greater(a, b), a, b)
# 若a>b,返回a对应位置的元素,否则返回b对应位置的元素
print("c:", c)
c: tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
np.random.RandomState.rand(n)
返回一个[0,1)之间的随机数,若n为空则返回标量。
rdm = np.random.RandomState(seed=1)
a = rdm.rand()
b = rdm.rand(2, 3)
print("a:", a)
print("b:", b)
a: 0.417022004702574
b: [[7.20324493e-01 1.14374817e-04 3.02332573e-01]
[1.46755891e-01 9.23385948e-02 1.86260211e-01]]
np.vstack(数组1, 数组2)
将两个数组按垂直方向叠加
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.vstack((a, b))
[[1 2 3]
[4 5 6]]
np.mgrid[]、x.ravel()、np.c_[]
np.mgrid[ 起始值 : 结束值 : 步长 ,起始值 : 结束值 : 步长 , … ]
x.ravel( ) 将x变为一维数组,“把 . 前变量拉直”
np.c_[ 数组1,数组2, … ] # 使返回的间隔数值点配对
# 生成等间隔数值点
x, y = np.mgrid[1:3:1, 2:4:0.5]
# 将x, y拉直,并合并配对为二维张量,生成二维坐标点
grid = np.c_[x.ravel(), y.ravel()]
print("x:\n", x)
print("y:\n", y)
print("x.ravel():\n", x.ravel())
print("y.ravel():\n", y.ravel())
print('grid:\n', grid)
x:
[[1. 1. 1. 1.]
[2. 2. 2. 2.]]
y:
[[2. 2.5 3. 3.5]
[2. 2.5 3. 3.5]]
x.ravel():
[1. 1. 1. 1. 2. 2. 2. 2.]
y.ravel():
[2. 2.5 3. 3.5 2. 2.5 3. 3.5]
grid:
[[1. 2. ]
[1. 2.5]
[1. 3. ]
[1. 3.5]
[2. 2. ]
[2. 2.5]
[2. 3. ]
[2. 3.5]]
二、神经网络复杂度
神经网络复杂度多用神经网络层数和神经网络参数的个数表示。
空间复杂度
层数 = 隐藏层的层数 + 1个输出层
(上图为2层神经网络)
总参数 = 总w + 总b
(上图34+4 + 42+2 = 26)
时间复杂度
乘加运算次数
(上图34 + 42 = 20)
指数衰减学习率
指数衰减学习率是先使用较大的学习率来快速得到一个较优的解,然后随着迭代的继续,逐步减小学习率,使得模型在训练后期 更加稳定。指数型学习率衰减法是最常用的衰减方法,在大量模型中都广泛使用。
指数衰减学习率 = 初始学习率 * 学习率衰减率( 当前轮数 / 多少轮衰减一次 )
激活函数
激活函数是用来加入非线性因素的,因为线性模型的表达能力不够。引入非线性激活函数,可使深层神经网络的表达能力更加强大。
常见的激活函数有:sigmoid,tanh,ReLU,Leaky ReLU,PReLU,RReLU, ELU(Exponential Linear Units),softplus,softsign,softmax等,下面介绍几个典型的激活函数
优秀的激活函数:
- 非线性: 激活函数非线性时,多层神经网络可逼近所有函数
- 可微性: 优化器大多用梯度下降更新参数
- 单调性: 当激活函数是单调的,能保证单层网络的损失函数是凸函数
- 近似恒等性: f(x)≈x当参数初始化为随机小值时,神经网络更稳定
激活函数输出值的范围
- 激活函数输出为有限值时,基于梯度的优化方法更稳定
- 激活函数输出为无限值时,建议调小学习率
Sigmoid函数
tf.nn. sigmoid(x)
- 易造成梯度消失
- 输出非0均值,收敛慢
- 幂运算复杂,训练时间长
Tanh函数
tf.math. tanh(x)
- 输出是0均值
- 易造成梯度消失
- 幂运算复杂,训练时间长
Relu函数
tf.nn.relu(x)
优点:
- 解决了梯度消失问题 (在正区间)
- 只需判断输入是否大于0,计算速度快
- 收敛速度远快于sigmoid和tanh
缺点:
- 输出非0均值,收敛慢
- Dead RelU问题:某些神经元可能永远不会
被激活,导致相应的参数永远不能被更新。
Leaky Relu函数
tf.nn.leaky_relu(x)
Leaky Relu有Relu的所有优点,外加不会有Dead Relu问题,但是在实际操作当中,并没有完全证明Leaky Relu总是好于Relu。
损失函数(loss)
预测值(y)与已知答案(y_)的差距
神经网络优化目标:loss最小
均方误差损失函数
均方误差(Mean Square Error)是回归问题最常用的损失函数。回归问题解决的是对具体数值的预测,比如房价预测、销量预测等。这些问题需要预测的不是一个事先定义好的类别,而是一个任意实数。均方误差定义如下:
其中yi为一个batch中第i个数据的真实值,而yi导为神经网络的预测值。
loss_mse = tf.reduce_mean(tf.square(y_ - y))
交叉熵损失函数
交叉熵(Cross Entropy)表征两个概率分布之间的距离,交叉熵越小说明二者分布越接近,是分类问题中使用较广泛的损失函数。
其中 代表数据的真实值, 代表神经网络的预测值。
tf.losses.categorical_crossentropy(y_,y)
loss_ce1 = tf.losses.categorical_crossentropy([1, 0], [0.6, 0.4])
loss_ce2 = tf.losses.categorical_crossentropy([1, 0], [0.8, 0.2])
print("loss_ce1:", loss_ce1)
print("loss_ce2:", loss_ce2)
loss_ce1: tf.Tensor(0.5108256, shape=(), dtype=float32)
loss_ce2: tf.Tensor(0.22314353, shape=(), dtype=float32)
对于多分类问题,神经网络的输出一般不是概率分布,因此需要引入softmax层,使得输出服从概率分布。TensorFlow中可计算交叉熵损失函数的API有:
TensorFlow API: tf.keras.losses.categorical_crossentropy TensorFlow API: tf.nn.softmax_cross_entropy_with_logits
TensorFlow API: tf.nn.sparse_softmax_cross_entropy_with_logits
自定义损失函数
根据具体任务和目的,可设计不同的损失函数。从老师课件和讲解中对于酸奶预测损失函数的设计,我们可以得知损失函数的定义能极大影响模型预测效果。好的损失函数设计对于模型训练能够起到良好的引导作用。
loss_zdy = tf.reduce_sum(tf.where(tf.greater(y,y_),COST(y-y_),PROFIT(y_-y)))
过拟合与欠拟合
欠拟合的解决方法:
- 增加输入特征项
- 增加网络参数
- 减少正则化参数
过拟合的解决方法:
- 数据清洗
- 增大训练集
- 采用正则化
- 增大正则化参数
正则化缓解过拟合
正则化在损失函数中引入模型复杂度指标,利用给W加权值,弱化了训练数据的噪声(一般不正则化b)
正则化的选择
L1正则化大概率会使很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度。
L2正则化会使参数很接近零但不为零,因此该方法可通过减小参数值的大小降低复杂度
神经网络参数优化器
优化算法可以分成一阶优化和二阶优化算法,其中一阶优化就是指的梯度算法及其变种,而二阶优化一般是用二阶导数(Hessian 矩阵)来计算,如牛顿法,由于需要计算Hessian阵和其逆矩阵,计算量较大,因此没有流行开来。这里主要总结一阶优化的各种梯度下降方法。
一阶动量:与梯度相关的函数
二阶动量:与梯度平方相关的函数
1. SGD(无momentum)
w1.assign_sub(lr * grads[0]) # 参数w1自更新
b1.assign_sub(lr * grads[1]) # 参数b自更新
2. SGDM(含momentum的SGD)
在SGD基础上增加一阶动量。
# sgd-momentun
m_w = beta * m_w + (1 - beta) * grads[0]
m_b = beta * m_b + (1 - beta) * grads[1]
w1.assign_sub(lr * m_w)
b1.assign_sub(lr * m_b)
3. Adagrad
在SGD基础上增加二阶动量
# adagrad
v_w += tf.square(grads[0])
v_b += tf.square(grads[1])
w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))
4. RMSProp
SGD基础上增加二阶动量
# rmsprop
v_w = beta * v_w + (1 - beta) * tf.square(grads[0])
v_b = beta * v_b + (1 - beta) * tf.square(grads[1])
w1.assign_sub(lr * grads[0] / tf.sqrt(v_w))
b1.assign_sub(lr * grads[1] / tf.sqrt(v_b))
5. Adam
同时结合SGDM一阶动量和RMSProp二阶动量
# adam
m_w = beta1 * m_w + (1 - beta1) * grads[0]
m_b = beta1 * m_b + (1 - beta1) * grads[1]
v_w = beta2 * v_w + (1 - beta2) * tf.square(grads[0])
v_b = beta2 * v_b + (1 - beta2) * tf.square(grads[1])
m_w_correction = m_w / (1 - tf.pow(beta1, int(global_step)))
m_b_correction = m_b / (1 - tf.pow(beta1, int(global_step)))
v_w_correction = v_w / (1 - tf.pow(beta2, int(global_step)))
v_b_correction = v_b / (1 - tf.pow(beta2, int(global_step)))
w1.assign_sub(lr * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(lr * m_b_correction / tf.sqrt(v_b_correction))