tf.nn.sigmoid或者tf.sigmoid
tf.math.tanh 或者tf.keras.activations.tanh
tf.nn.relu或者tf.keras.activations.relu
前者适用于无keras包,后者在keras搭建中使用
假设有n个输出节点
MSE:
y_onehot = tf.one_hot(y_train, depth=n)
loss = tf.reduce_mean(tf.square(y_onehot - y))
CE:
y_onehot = tf.one_hot(y_train, depth=n)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_onehot, y))
Tf.train.GradientDescentOptimizer(learning_rate, use_locking=False,
name='GradientDescent)
也就是我们最朴素的方法
v[t] = -ε*▽f(θ[t])
Θ[t+1] = θ[t] +v[t]
SGD全名 stochastic gradient descent, 即随机梯度下降
即从样本中抽取子样本进行梯度训练
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(10)
batch()即划分为子样本进行训练
缺点:
1:由于是抽取,因此不可避免的,得到的梯度肯定有误差.因此学习速率需要逐渐减小.否则模型无法收敛 2:因为误差,所以每一次迭代的梯度受抽样的影响比较大,也就是说梯度含有比较大的噪声,不能很好的反映真实梯度.
优化方法:采用学习率衰减
learning_rate = learning_rate_base * learning_rate_decay ** (epoch / learning_rate_step)
最常见的标准化方法就是Z标准化,也是SPSS中最为常用的标准化方法,spss默认的标准化方法就是z-score标准化。
也叫标准差标准化,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。
经过处理的数据符合标准正态分布,即均值为0,标准差为1,其转化函数为:
x* = (x - μ ) / σ
其中μ为所有样本数据的均值,σ为所有样本数据的标准差。
z-score标准化方法适用于属性A的最大值和最小值未知的情况,或有超出取值范围的离群数据的情况。该种归一化方式要求原始数据的分布可以近似为高斯分布,否则归一化的效果会变得很糟糕。
SGD方法的一个缺点是,其更新方向完全依赖于当前的batch,因而其更新十分不稳定,每次迭代计算的梯度含有比较大的噪音。解决这一问题的一个简单的做法便是引入momentum。
momentum即动量,它模拟的是物体运动时的惯性,即更新的时候在一定程度上保留之前更新的方向,同时利用当前batch的梯度微调最终的更新方向。这样一来,可以在一定程度上增加稳定性,从而学习地更快,并且还有一定摆脱局部最优的能力。
特点:
beta = 0.9
m_w = beta * m_w + (1 - beta) * grads[0]
m_b = beta * m_b + (1 - beta) * grads[1]
w1.assign_sub(learning_rate * m_w)
b1.assign_sub(learning_rate * m_b)
优点: 能够实现学习率的自动更改。如果这次梯度大,那么学习速率衰减的就快一些;如果这次梯度小,那么学习速率衰减的慢一些
缺点: 任然要设置一个变量ϵ ,经验表明,在普通算法中也许效果不错,会使得学习率单调递减至0,可能会使得训练过程提前结束,即便后续还有数据也无法学到必要的知识。
v_w += tf.square(grads[0])
v_b += tf.square(grads[1])
w1.assign_sub(learning_rate * grads[0] / tf.sqrt(v_w))
b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))
从上面的代码可以看到 grads[0] / tf.sqrt(v_w) v_w += tf.square(grads[0])
从这数学式中我们可以看到 相当于 grads[0]/ tf.square(grads[0])
即若**grads[i]**过大,则我们要缩小梯度
grads过小,我们要放大梯度
当然如果把 learning_rate/tf.sqrt(v_w) 整体看作学习率,也说明若如果这次梯度大,那么学习速率衰减的就快一些;如果这次梯度小,那么学习速率衰减的慢一些
由于AdaGrad单调递减的学习率变化过于激进,我们考虑一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度。这也就是AdaDelta名称中Delta的来历。
修改的思路很简单。前面我们讲到,指数移动平均值大约就是过去一段时间的平均值,因此我们用这一方法来计算二阶累积动量:这就避免了二阶动量持续累积、导致训练过程提前结束的问题了。
beta = 0.9
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(learning_rate * grads[0] / tf.sqrt(v_w))
b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))
我们可以看到,对于历史时间的梯度,每次都会*beta,这会导致越远历史占比越小,近似达到动态窗口的目的。
Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
通俗来讲:
是前述方法的集大成者。我们看到,SGD-M在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive +Momentum。
global_step = global_step.assign_add(1)
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(learning_rate * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(learning_rate * m_b_correction / tf.sqrt(v_b_correction))
将鸢尾花数据集合带入测试,得到结果如下:【准确率都是100%】
SGD:
loss: 0.12647321820259094
time: 12.007081508636475
momentum-SGD:
loss: 0.12340985238552094
time: 12.843583583831787
AdaGrad:
loss: 0.10842594504356384
time: 12.665419578552246
AdaDelta:
loss: 0.04672025144100189
time: 13.282380819320679
Adam:
loss: 0.04928009212017059
time: 15.38596796989441
可以看到AdaDelta和Adam表现最好,但是时间也是最多。
所以在不同时候要选用不同的方法
下面这篇博文也许能告诉你为什么有些时候我们仍要用SGD,类似于老式相机和美颜手机的区别
https://blog.csdn.net/jiachen0212/article/details/80086926
import tensorflow as tf
import os
from sklearn import datasets
from matplotlib import pyplot as plt
import numpy as np
import time
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 只显示error,不显示其他信息
def standardize(train):
# 数据标准化(标准正态分布)
x_data = train.T
for i in range(4):
x_data[i] = (x_data[i] - np.mean(x_data[i])) / np.std(x_data[i])
return x_data.T
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
x_data = standardize(x_data)
# 随机打乱数据
np.random.seed(116)
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)
# from_tensor_slices函数切分传入的 Tensor 的第一个维度,生成相应的 dataset
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(10)
# seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
#SGD的参数
# learning_rate_step = 10
# learning_rate_decay = 0.8
# learning_rate_base = 1
# global_step = epoch = 500
#momentum-SGD的参数
# learning_rate_step = 10
# learning_rate_decay = 0.8
# learning_rate_base = 1
# global_step = epoch = 500
# m_w, m_b = 0, 0
#adagrad的参数
# learning_rate_step = 10
# learning_rate_decay = 0.8
# learning_rate_base = 1
# v_w, v_b = 0, 0
# global_step = epoch = 500
#adadelta的参数
# learning_rate_step = 10
# learning_rate_decay = 0.8
# learning_rate_base = 1
# v_w, v_b = 0, 0
# global_step = epoch = 500
#adam的参数
learning_rate_step = 10
learning_rate_decay = 0.8
learning_rate_base = 1
delta_w, delta_b = 0, 0
beta = 0.9
global_step = tf.Variable(0, trainable=False)
m_w, m_b = 0, 0
v_w, v_b = 0, 0
beta1, beta2 = 0.9, 0.999
epoch = 500
train_loss_results = []
test_acc = []
lr = []
loss_all = 0
now_time = time.time()
for epoch in range(epoch):
learning_rate = learning_rate_base * learning_rate_decay ** (epoch / learning_rate_step)
lr.append(learning_rate)
for step, (x_train, y_train) in enumerate(train_db):
with tf.GradientTape() as tape:
y = tf.matmul(x_train, w1) + b1
# y = tf.math.tanh(y)
y_onehot = tf.one_hot(y_train, depth=3)
# loss = tf.reduce_mean(tf.square(y_onehot - y))
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_onehot, y))
loss_all += loss.numpy()
grads = tape.gradient(loss, [w1, b1])
#SGD
# w1.assign_sub(learning_rate * grads[0])
# b1.assign_sub(learning_rate * grads[1])
#momentum-SGD
# beta = 0.9
# m_w = beta * m_w + (1 - beta) * grads[0]
# m_b = beta * m_b + (1 - beta) * grads[1]
# w1.assign_sub(learning_rate * m_w)
# b1.assign_sub(learning_rate * m_b)
# adagrad
# v_w += tf.square(grads[0])
# v_b += tf.square(grads[1])
# w1.assign_sub(learning_rate * grads[0] / tf.sqrt(v_w))
# b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))
# adadelta
# beta = 0.9
# 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(learning_rate * grads[0] / tf.sqrt(v_w))
# b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))
# adam
global_step = global_step.assign_add(1)
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(learning_rate * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(learning_rate * m_b_correction / tf.sqrt(v_b_correction))
if step % 10 == 0:
print("step=", step, 'loss:', float(loss))
print("lr=", learning_rate)
train_loss_results.append(loss_all / 3)
loss_all = 0
# test(做测试)
total_correct, total_number = 0, 0
for step, (x_test, y_test) in enumerate(test_db):
y = tf.matmul(x_test, w1) + b1
y = tf.nn.sigmoid(y)
pred = tf.argmax(y, axis=1)
# 因为pred的dtype为int64,在计算correct时会出错,所以需要将它转化为int32
pred = tf.cast(pred, dtype=tf.int32)
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum(correct)
total_correct += int(correct)
total_number += x_test.shape[0]
acc = total_correct / total_number
test_acc.append(acc)
print("test_acc:", acc)
print("---------------------")
total_time = time.time() - now_time
print("total_time", total_time)
# 绘制 loss 曲线
plt.title('Loss Function Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(train_loss_results, label="$Loss$")
plt.legend()
plt.show()
# 绘制 Accuracy 曲线
plt.title('Acc Curve')
plt.xlabel('Epoch')
plt.ylabel('Acc')
plt.plot(test_acc, label="$Accuracy$")
plt.legend()
plt.show()
# 绘制 Learning_rate 曲线
plt.title('Learning Rate Curve')
plt.xlabel('Global steps')
plt.ylabel('Learning rate')
plt.plot(range(epoch + 1), lr, label="$lr$")
plt.legend()
plt.show()