赞
踩
先看一个传统方法手动实现线性回归和MSE损失函数的方案:
- import tensorflow as tf
- import numpy as np
- from sklearn.datasets import fetch_california_housing
- from sklearn.preprocessing import StandardScaler
-
- #多元线性回归是一个凸函数 ,所以能找到全局最优解
- #神经网络只有局部最优解
- n_epochs = 1000#把样本集数据学习1000次
- learning_rate = 0.01 #步长 学习率 不能太大 太大容易来回震荡 太小 耗时间,跳不出局部最优解
- #可以写learn_rate动态变化,随着迭代次数越来越大 ,学习率越来越小 learning_rate/n_epoches
- housing = fetch_california_housing()
- m, n = housing.data.shape
- housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
- # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
- #归一化可以最快的找到最优解
- #常用的归一化方式:
- # 最大最小值归一化 (x-min)/(max-min)
- # 方差归一化 x/方差
- # 均值归一化 x-均值 结果有正有负 可以使调整时的速度越来越快。
- scaler = StandardScaler().fit(housing_data_plus_bias) #创建一个归一化对象
- scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias) #真正执行 因为来源于sklearn所以会直接执行,不会延迟。
- # 归一化后的数据,均值为0,方差为1:
-
-
- X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
- y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
-
- # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
- theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta') #theta是参数 W0-Wn 一列 按照-1.0到1.0随机给
- y_pred = tf.matmul(X, theta, name="predictions")#相乘 m行一列
- error = y_pred - y #列向量和列向量相减 是一组数
- mse = tf.reduce_mean(tf.square(error), name="mse")#误差平方加和,最小二乘 平方均值损失函数 手动实现
- # 梯度的公式:(y_pred - y) * xj i代表行 j代表列
- gradients = 2/m * tf.matmul(tf.transpose(X), error)#矩阵和向量相乘会得到新的向量 一组梯度
- # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
- training_op = tf.assign(theta, theta - learning_rate * gradients)#assigin赋值 算一组w
- # training_op实际上就是需要迭代的公式
-
- init = tf.global_variables_initializer()
-
- with tf.Session() as sess:
- sess.run(init) #初始化
-
- for epoch in range(n_epochs):#迭代1000次
- if epoch % 100 == 0:
- print("Epoch", epoch, "MSE = ", mse.eval())#每运行100次的时候输出
- sess.run(training_op)
-
- best_theta = theta.eval()#最后的w参数值
- print(best_theta)
-
看下结果:
当迭代很多次的时候,MSE到4.8左右的位置不再变了,所以我们可以通过评估MSE损失函数,我们可以找到它的最优解。做深度学习,很多时候是给很大的迭代的次数,让它去算一下损失函数,然后来评估。做回归就是用MSE来进行评估;如果做分类,就是ACC或者AUC来进行评估。
显然上面代码写起来非常挺麻烦,因为在里面初始化θ之后,进行正向传播,但是在算梯度的时候,得先知道做线性回归的梯度的公式,如果不知道公式,代码就没法写出来,不知道复杂函数的导函数,那么能不能让它自动算?
前面的代码执行的不错,但是它需要数学上通过损失函数MSE来求导梯度,我们得先把它推出来。
在线性回归的例子中,这样是可以的,看起来通过数学公式来求解不难,线性回归的公式很简单y=wTx,如果是神经网络,多层,每层上面有很多神经元,它的写出来很崩溃。所以反向传播可以自动的求导。
如果是深度学习,我们很难这样去做,会比较头疼,很容易推倒出错。
幸运的是,tensorflow提供了autodiff的特性,可以自动的有效的计算梯度。即reverse-mode autodiff。话不多少,先上代码:
- import tensorflow as tf
- import numpy as np
- from sklearn.datasets import fetch_california_housing
- from sklearn.preprocessing import StandardScaler
-
- # 前面的代码执行的不错,但是它需要数学上通过损失函数MSE来求导梯度
- # 在线性回归的例子中,这样是可以的,看起来通过数学公式去求解不难
- # 但是如果是深度学习,我们很难这样去做,会比较头疼,会很容易出错
- # 幸运的是,TensorFlow提供的autodiff特性可以自动的并有效的计算梯度为我们
- # reverse-mode autodiff
-
-
- n_epochs = 1000
- learning_rate = 0.01
-
- housing = fetch_california_housing()
- m, n = housing.data.shape
- housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
- # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
- scaler = StandardScaler().fit(housing_data_plus_bias)
- scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)
-
- X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
- y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
-
- # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
- theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
- y_pred = tf.matmul(X, theta, name="predictions")
- error = y_pred - y
- mse = tf.reduce_mean(tf.square(error), name="mse")
- # 梯度的公式:(y_pred - y) * xj
- # gradients = 2/m * tf.matmul(tf.transpose(X), error)
- gradients = tf.gradients(mse, [theta])[0] #损失函数对theta求偏导
- print(gradients)
- # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
- training_op = tf.assign(theta, theta - learning_rate * gradients)
-
- init = tf.global_variables_initializer()
- with tf.Session() as sess:
- sess.run(init)
-
- for epoch in range(n_epochs):
- if epoch % 100 == 0:
- print("Epoch", epoch, "MSE = ", mse.eval())
- sess.run(training_op)
-
- best_theta = theta.eval()
- print(best_theta)
-
- # tf.gradients()实现ys对xs求导
- # 求导返回值是一个list,list的长度等于len(xs)
- # 假设返回值是[grad1, grad2, grad3],ys=[y1, y2],xs=[x1, x2, x3]。则,真实的计算过程为:
- # grad1=y1/x1+y2/x1
- # grad2=y1/x2+y2/x2
- # grad3=y1/x3+y2/x3
详细解释下这个代码:
- n_epochs = 1000
- learning_rate = 0.01
-
- housing = fetch_california_housing()
- m, n = housing.data.shape
- housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
- # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
- scaler = StandardScaler().fit(housing_data_plus_bias)
- scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)
该有的参数少不了,超参数、迭代的次数、学习率,然后读数据,然后加上x0,做规一化。
- X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
- y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
-
- # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
- theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
- y_pred = tf.matmul(X, theta, name="predictions")
- error = y_pred - y
- mse = tf.reduce_mean(tf.square(error), name="mse")
x,y创建两个tensor,第一步随机初始化theta,然后进行正向传播,简单单层的y=wTx。正向传播一行搞定。
- gradients = tf.gradients(mse, [theta])[0] #损失函数对theta求偏导
- print(gradients)
- # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
- training_op = tf.assign(theta, theta - learning_rate * gradients)
-
- init = tf.global_variables_initializer()
- with tf.Session() as sess:
- 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的话,这个就是梯度下降。可以用其它方式来做。
我们看下代码:
- import tensorflow as tf
- import numpy as np
- from sklearn.datasets import fetch_california_housing
- from sklearn.preprocessing import StandardScaler
-
-
- # TensorFlow为我们去计算梯度,但是同时也给了我们更方便的求解方式
- # 它提供给我们与众不同的,有创意的一些优化器,包括梯度下降优化器
- # 替换前面代码相应的行,并且一切工作正常
-
-
- n_epochs = 1000
- learning_rate = 0.01
-
- housing = fetch_california_housing()
- m, n = housing.data.shape
- housing_data_plus_bias = np.c_[np.ones((m, 1)), housing.data]
- # 可以使用TensorFlow或者Numpy或者sklearn的StandardScaler去进行归一化
- scaler = StandardScaler().fit(housing_data_plus_bias)
- scaled_housing_data_plus_bias = scaler.transform(housing_data_plus_bias)
-
- X = tf.constant(scaled_housing_data_plus_bias, dtype=tf.float32, name='X')
- y = tf.constant(housing.target.reshape(-1, 1), dtype=tf.float32, name='y')
-
- # random_uniform函数创建图里一个节点包含随机数值,给定它的形状和取值范围,就像numpy里面rand()函数
- theta = tf.Variable(tf.random_uniform([n + 1, 1], -1.0, 1.0), name='theta')
- y_pred = tf.matmul(X, theta, name="predictions")
- error = y_pred - y
- mse = tf.reduce_mean(tf.square(error), name="mse")
- # 梯度的公式:(y_pred - y) * xj
- # gradients = 2/m * tf.matmul(tf.transpose(X), error)
- # gradients = tf.gradients(mse, [theta])[0]
- # 赋值函数对于BGD来说就是 theta_new = theta - (learning_rate * gradients)
- # training_op = tf.assign(theta, theta - learning_rate * gradients)
-
- optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
- # MomentumOptimizer收敛会比梯度下降更快
- # optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
- training_op = optimizer.minimize(mse)#去减小误差
-
- init = tf.global_variables_initializer()
-
- with tf.Session() as sess:
- sess.run(init)
-
- for epoch in range(n_epochs):
- if epoch % 100 == 0:
- print("Epoch", epoch, "MSE = ", mse.eval())
- sess.run(training_op)
-
- best_theta = theta.eval()
- print(best_theta)
-
tensorflow为我们计算梯度,同时也给我们更方便的调节方式,它提供给我们与众不同的、有创意的一些优化器,包括梯度下降优化器,它会方便很多,会替换前面的部分代码,并且可以使计算一切正常。
我们需要设定一些超参数,就是排列组合,找到Loss function最小时的那组超参数的结果,该有的这些东西都不变,这些代码都没有任何的差别。
之前是tf.gradients来求梯度,然后应用梯度下降的公式,然后通过assign重新赋值theta。
现在换一种方式,内部封装好的,叫 tf.train.GradientDescentOptimizer。
- optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
- # MomentumOptimizer收敛会比梯度下降更快
- # optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate, momentum=0.9)
- 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的传值不符合我们的约定,会报错。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。