当前位置:   article > 正文

大白话5分钟带你走进人工智能-第38节神经网络之TensorFlow利用梯度下降法求解最优解(6)_tensorflow 如何从海量数据中选取最优解

tensorflow 如何从海量数据中选取最优解

先看一个传统方法手动实现线性回归和MSE损失函数的方案:

  1. import tensorflow as tf
  2. import numpy as np
  3. from sklearn.datasets import fetch_california_housing
  4. from sklearn.preprocessing import StandardScaler
  5. #多元线性回归是一个凸函数 ,所以能找到全局最优解
  6. #神经网络只有局部最优解
  7. n_epochs = 1000#把样本集数据学习1000
  8. learning_rate = 0.01 #步长 学习率 不能太大 太大容易来回震荡 太小 耗时间,跳不出局部最优解
  9. #可以写learn_rate动态变化,随着迭代次数越来越大 ,学习率越来越小 learning_rate/n_epoches
  10. housing = fetch_california_housing()
  11. m, n = housing.data.shape
  12. housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
  13. # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
  14. #归一化可以最快的找到最优解
  15. #常用的归一化方式:
  16. # 最大最小值归一化 (x-min)/(max-min)
  17. # 方差归一化 x/方差
  18. # 均值归一化 x-均值 结果有正有负 可以使调整时的速度越来越快。
  19. scaler = StandardScaler().fit(housing_data_plus_bias) #创建一个归一化对象
  20. scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias) #真正执行 因为来源于sklearn所以会直接执行,不会延迟。
  21. # 归一化后的数据,均值为0,方差为1
  22. X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
  23. y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
  24. # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
  25. theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta') #theta是参数 W0-Wn 一列 按照-1.01.0随机给
  26. y_pred = tf.matmul(X, theta, name="predictions")#相乘 m行一列
  27. error = y_pred - y #列向量和列向量相减 是一组数
  28. mse = tf.reduce_mean(tf.square(error), name="mse")#误差平方加和,最小二乘 平方均值损失函数 手动实现
  29. # 梯度的公式:(y_pred - y) * xj i代表行 j代表列
  30. gradients = 2/m * tf.matmul(tf.transpose(X), error)#矩阵和向量相乘会得到新的向量 一组梯度
  31. # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
  32. training_op = tf.assign(theta, theta - learning_rate * gradients)#assigin赋值 算一组w
  33. # training_op实际上就是需要迭代的公式
  34. init = tf.global_variables_initializer()
  35. with tf.Session() as sess:
  36. sess.run(init) #初始化
  37. for epoch in range(n_epochs):#迭代1000
  38. if epoch % 100 == 0:
  39. print("Epoch", epoch, "MSE = ", mse.eval())#每运行100次的时候输出
  40. sess.run(training_op)
  41. best_theta = theta.eval()#最后的w参数值
  42. print(best_theta)

看下结果:

当迭代很多次的时候,MSE到4.8左右的位置不再变了,所以我们可以通过评估MSE损失函数,我们可以找到它的最优解。做深度学习,很多时候是给很大的迭代的次数,让它去算一下损失函数,然后来评估。做回归就是用MSE来进行评估;如果做分类,就是ACC或者AUC来进行评估。

显然上面代码写起来非常挺麻烦,因为在里面初始化θ之后,进行正向传播,但是在算梯度的时候,得先知道做线性回归的梯度的公式,如果不知道公式,代码就没法写出来,不知道复杂函数的导函数,那么能不能让它自动算?

前面的代码执行的不错,但是它需要数学上通过损失函数MSE来求导梯度,我们得先把它推出来。

在线性回归的例子中,这样是可以的,看起来通过数学公式来求解不难,线性回归的公式很简单y=wTx,如果是神经网络,多层,每层上面有很多神经元,它的写出来很崩溃。所以反向传播可以自动的求导。

如果是深度学习,我们很难这样去做,会比较头疼,很容易推倒出错。

幸运的是,tensorflow提供了autodiff的特性,可以自动的有效的计算梯度。即reverse-mode autodiff。话不多少,先上代码:

  1. import tensorflow as tf
  2. import numpy as np
  3. from sklearn.datasets import fetch_california_housing
  4. from sklearn.preprocessing import StandardScaler
  5. # 前面的代码执行的不错,但是它需要数学上通过损失函数MSE来求导梯度
  6. # 在线性回归的例子中,这样是可以的,看起来通过数学公式去求解不难
  7. # 但是如果是深度学习,我们很难这样去做,会比较头疼,会很容易出错
  8. # 幸运的是,TensorFlow提供的autodiff特性可以自动的并有效的计算梯度为我们
  9. # reverse-mode autodiff
  10. n_epochs = 1000
  11. learning_rate = 0.01
  12. housing = fetch_california_housing()
  13. m, n = housing.data.shape
  14. housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
  15. # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
  16. scaler = StandardScaler().fit(housing_data_plus_bias)
  17. scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)
  18. X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
  19. y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
  20. # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
  21. theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
  22. y_pred = tf.matmul(X, theta, name="predictions")
  23. error = y_pred - y
  24. mse = tf.reduce_mean(tf.square(error), name="mse")
  25. # 梯度的公式:(y_pred - y) * xj
  26. # gradients = 2/m * tf.matmul(tf.transpose(X), error)
  27. gradients = tf.gradients(mse, [theta])[0] #损失函数对theta求偏导
  28. print(gradients)
  29. # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
  30. training_op = tf.assign(theta, theta - learning_rate * gradients)
  31. init = tf.global_variables_initializer()
  32. with tf.Session() as sess:
  33. sess.run(init)
  34. for epoch in range(n_epochs):
  35. if epoch % 100 == 0:
  36. print("Epoch", epoch, "MSE = ", mse.eval())
  37. sess.run(training_op)
  38. best_theta = theta.eval()
  39. print(best_theta)
  40. # tf.gradients()实现ys对xs求导
  41. # 求导返回值是一个list,list的长度等于len(xs)
  42. # 假设返回值是[grad1, grad2, grad3],ys=[y1, y2],xs=[x1, x2, x3]。则,真实的计算过程为:
  43. # grad1=y1/x1+y2/x1
  44. # grad2=y1/x2+y2/x2
  45. # grad3=y1/x3+y2/x3

详细解释下这个代码:

  1. n_epochs = 1000
  2. learning_rate = 0.01
  3. housing = fetch_california_housing()
  4. m, n = housing.data.shape
  5. housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
  6. # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
  7. scaler = StandardScaler().fit(housing_data_plus_bias)
  8. scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)

该有的参数少不了,超参数、迭代的次数、学习率,然后读数据,然后加上x0,做规一化。

  1. X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
  2. y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
  3. # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
  4. theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
  5. y_pred = tf.matmul(X, theta, name="predictions")
  6. error = y_pred - y
  7. mse = tf.reduce_mean(tf.square(error), name="mse")

x,y创建两个tensor,第一步随机初始化theta,然后进行正向传播,简单单层的y=wTx。正向传播一行搞定。

  1. gradients = tf.gradients(mse, [theta])[0] #损失函数对theta求偏导
  2. print(gradients)
  3. # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
  4. training_op = tf.assign(theta, theta - learning_rate * gradients)
  5. init = tf.global_variables_initializer()
  6. with tf.Session() as sess:
  7. sess.run(init)

梯度求解这一步,不在需要之前自己写公式了,用的方法叫做tf.gradients,只需要告诉它Loss function是什么,或者要对哪个函数来求偏导,然后要对谁来求偏导,中括号里的theta不是一个数,不是一个变量。我们看下到底是什么?

theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')

创建的是n+1行1列的w矩阵,把w矩阵作为theta,相当于会对theta里面的每一个值,对Loss function损失函数来求偏导。

gradients = tf.gradients(mse, [theta])[0] #损失函数对theta求偏导

[0]把梯度取出来,就是我们得到的梯度,接着对它进行重新赋值。

在这里不用再去管导函数是什么了,不用自己手推,只要知道原来的函数是什么样子,再告诉它求哪些变量的梯度,通过tf.gradients就搞定了。求得最优解和刚才是一样的。前面讲的反向传播,就是讲tf.gradients到底是怎么做的。

那能不能把tf.gradients求梯度和assign重新改变theta的公式,再给它简化,现在得分两层来写,第一行是求梯度,第二是如何来应用梯度。更改w权重的方式不一样,就意味着最优解的算法不一样。

咱们这里面如果来实现的是wt+1=wt-αg的话,这个就是梯度下降。可以用其它方式来做。

我们看下代码:

  1. import tensorflow as tf
  2. import numpy as np
  3. from sklearn.datasets import fetch_california_housing
  4. from sklearn.preprocessing import StandardScaler
  5. # TensorFlow为我们去计算梯度,但是同时也给了我们更方便的求解方式
  6. # 它提供给我们与众不同的,有创意的一些优化器,包括梯度下降优化器
  7. # 替换前面代码相应的行,并且一切工作正常
  8. n_epochs = 1000
  9. learning_rate = 0.01
  10. housing = fetch_california_housing()
  11. m, n = housing.data.shape
  12. housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
  13. # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
  14. scaler = StandardScaler().fit(housing_data_plus_bias)
  15. scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)
  16. X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
  17. y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
  18. # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
  19. theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
  20. y_pred = tf.matmul(X, theta, name="predictions")
  21. error = y_pred - y
  22. mse = tf.reduce_mean(tf.square(error), name="mse")
  23. # 梯度的公式:(y_pred - y) * xj
  24. # gradients = 2/m * tf.matmul(tf.transpose(X), error)
  25. # gradients = tf.gradients(mse, [theta])[0]
  26. # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
  27. # training_op = tf.assign(theta, theta - learning_rate * gradients)
  28. optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
  29. # MomentumOptimizer收敛会比梯度下降更快
  30. # optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
  31. training_op = optimizer.minimize(mse)#去减小误差
  32. init = tf.global_variables_initializer()
  33. with tf.Session() as sess:
  34. sess.run(init)
  35. for epoch in range(n_epochs):
  36. if epoch % 100 == 0:
  37. print("Epoch", epoch, "MSE = ", mse.eval())
  38. sess.run(training_op)
  39. best_theta = theta.eval()
  40. print(best_theta)

tensorflow为我们计算梯度,同时也给我们更方便的调节方式,它提供给我们与众不同的、有创意的一些优化器,包括梯度下降优化器,它会方便很多,会替换前面的部分代码,并且可以使计算一切正常。

我们需要设定一些超参数,就是排列组合,找到Loss function最小时的那组超参数的结果,该有的这些东西都不变,这些代码都没有任何的差别。

之前是tf.gradients来求梯度,然后应用梯度下降的公式,然后通过assign重新赋值theta。

现在换一种方式,内部封装好的,叫 tf.train.GradientDescentOptimizer。

  1. optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
  2. # MomentumOptimizer收敛会比梯度下降更快
  3. # optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
  4. training_op = optimizer.minimize(mse)#去减小误差

optimizer优化器,只要传入学习率就够了。然后获得一个optimizer优化器的对象,然后通过optimizer. minimize通过优化器最小化mse损失,这一步是一个操作、一个逻辑。

Minimize这个函数里面内部会做两件事情,第一件事情是计算梯度,第二步使用梯度更新w参数。

Mini-Batch梯度下降。

现在的代码每一次循环迭代的时候,它首先把x拿来跟theta进行计算,然后一路往下执行,那么这个地方的x是所有的数据,还是一部分数据?每一次迭代的时候,它都要把它赋给x,再去算,所以每次迭代用的是全量的数据这种梯度下降叫BGD批量梯度下降,刚才是SGD随机梯度下降,一部分数据去做梯度下降。

现在让我们让我们修改前面的代码去实现,为了去实现这个Mini-Batch梯度下降,我们需要一种方式去取代X和y在每一次迭代中,使用一小批数据,最简单的方式去做到这个是去使用placeholder节点, 这些节点特点是它们不真正的计算,它们只是在执行过程中你要它们输出数据的时候去输出数据,它们会传输训练数据给TensorFlow在训练的时候, 如果在运行过程中你不给它们指定数据,你会得到一个异常,需要做的是使用placeholder()并且给输出的tensor指定数据类型,也可以选择指定形状,如果你指定None对于某一个维度,它的意思代表任意大小你把形状设置成多少行、多少列,那就意味着未来给a传数据的时候,传的数据可就以变成多少行、多少列,如果转换不成,它也会报错。这是定义好的输入数据的协议。

比如下面代码:

import tensorflow as tf
A = tf.placeholder(tf.float32, shape=(None, 3))
B = A + 5

with tf.Session() as sess:
    B_val_1 = B.eval(feed_dict={A: [[1, 2, 3]]})
    B_val_2 = B.eval(feed_dict={A: [['4', 5, 6],
                                    [7, 8, 9]]})

print(B_val_1)
print(B_val_2)

解释下:

A是一个placeholder定义的3列,多少行没关系的变量。

b=a+5,是一个tensor的逻辑。

第一次A传一个特殊的超参数 feed_dict ,传一个1行3列的数据。第二次A传一个2行3列的数据。

如果A的传值不符合我们的约定,会报错。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/103963?site
推荐阅读
相关标签
  

闽ICP备14008679号