【问题标题】:What does the TensowFlow GradientDescentOptimizer do in this example?在这个例子中,TensowFlow GradientDescentOptimizer 做了什么?
【发布时间】:2018-03-23 18:13:11
【问题描述】:

我正在尝试做Stanfords CS20: TensorFlow for Deep Learning Research 课程。前 2 节课很好地介绍了低级管道和计算框架(坦率地说,官方的介绍性教程似乎跳过了,原因我只能理解为虐待狂)。在lecture 3 中,它开始执行线性回归,并且对我来说似乎是一个相当大的认知飞跃。它不是在张量计算中使用session.run,而是在 GradientDescentOptimizer 上进行。

sess.run(optimizer, feed_dict={X: x, Y:y}) 

完整的代码在lecture 3 notes的第3页。

编辑:代码和数据也可用at this github - 代码在examples/03_linreg_placeholder.py 中可用,数据在examples/data/birth_life_2010.txt 中可用

编辑:根据要求代码如下

import tensorflow as tf

import utils

DATA_FILE = "data/birth_life_2010.f[txt"

# Step 1: read in data from the .txt file
# data is a numpy array of shape (190, 2), each row is a datapoint
data, n_samples = utils.read_birth_life_data(DATA_FILE)

# Step 2: create placeholders for X (birth rate) and Y (life expectancy)
X = tf.placeholder(tf.float32, name='X')
Y = tf.placeholder(tf.float32, name='Y')

# Step 3: create weight and bias, initialized to 0
w = tf.get_variable('weights', initializer=tf.constant(0.0))
b = tf.get_variable('bias', initializer=tf.constant(0.0))

# Step 4: construct model to predict Y (life expectancy from birth rate)
Y_predicted = w * X + b 

# Step 5: use the square error as the loss function
loss = tf.square(Y - Y_predicted, name='loss')

# Step 6: using gradient descent with learning rate of 0.01 to minimize loss
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss)

with tf.Session() as sess:
    # Step 7: initialize the necessary variables, in this case, w and b
    sess.run(tf.global_variables_initializer()) 

    # Step 8: train the model
    for i in range(100): # run 100 epochs
        for x, y in data:
            # Session runs train_op to minimize loss
            sess.run(optimizer, feed_dict={X: x, Y:y}) 

    # Step 9: output the values of w and b
    w_out, b_out = sess.run([w, b]) 

我已经完成了coursera machine learning course 课程,所以我(认为)我理解梯度下降的概念。但我对这种特定情况下发生的事情感到很迷茫。

我期望发生的事情:

  • 计算梯度(通过微积分或数值方法)
  • 计算参数变化(alpha 乘以整个数据集的预测值与实际值)
  • 调整参数
  • 重复上述 N 次(在本例中为 100 次,共 100 个 epoch)

我知道在实践中你会应用批处理和子集之类的东西,但在这种情况下,我相信这只是循环整个数据集 100 次。

我之前可以(并且已经)实现了这一点。但我正在努力理解上面的代码如何实现这一点。一方面是在每个数据点上调用优化器(即它在 100 个时期的内部循环中,然后是每个数据点)。我会期待一个包含整个数据集的优化调用。

问题 1 - 梯度调整是在整个数据集上运行 100 次,还是在整个数据集上运行 100 次,每批 1 次(例如 100*n 次,例如 n 次)?

问题 2 - 优化器如何“知道”如何调整 w 和 b?它只提供了损失张量 - 它是否通过图表回读并只是“嗯,w 和 b 是唯一的变量,所以我将摆脱这些”

问题 2b - 如果是这样,如果你放入其他变量会发生什么?还是更复杂的功能?它是否只是自动为先前图中的每个变量自动计算梯度调整**

问题 2c - 据此,我尝试按照教程第 3 页中的建议调整二次表达式,但最终损失更高。这是正常的吗?该教程似乎表明它应该更好。至少我预计情况不会更糟 - 这是否会受到超参数变化的影响?

编辑:我尝试调整二次方的完整代码在这里。这与上面的第 28、29、30 和 34 行修改为使用二次预测器不同。这些编辑(我的解释)是第 4 页上 lecture 3 注释中建议的内容

""" Solution for simple linear regression example using placeholders
Created by Chip Huyen (chiphuyen@cs.stanford.edu)
CS20: "TensorFlow for Deep Learning Research"
cs20.stanford.edu
Lecture 03
"""
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import time

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

import utils

DATA_FILE = 'data/birth_life_2010.txt'

# Step 1: read in data from the .txt file
data, n_samples = utils.read_birth_life_data(DATA_FILE)

# Step 2: create placeholders for X (birth rate) and Y (life expectancy)
X = tf.placeholder(tf.float32, name='X')
Y = tf.placeholder(tf.float32, name='Y')

# Step 3: create weight and bias, initialized to 0
# w = tf.get_variable('weights', initializer=tf.constant(0.0)) old single weight
w = tf.get_variable('weights_1', initializer=tf.constant(0.0))
u = tf.get_variable('weights_2', initializer=tf.constant(0.0))
b = tf.get_variable('bias', initializer=tf.constant(0.0))

# Step 4: build model to predict Y
#Y_predicted = w * X + b  #linear
Y_predicted = w * X * X + X * u + b  #quadratic
#Y_predicted = w  # test of nonsense


# Step 5: use the squared error as the loss function
# you can use either mean squared error or Huber loss
loss = tf.square(Y - Y_predicted, name='loss')
#loss = utils.huber_loss(Y, Y_predicted)

# Step 6: using gradient descent with learning rate of 0.001 to minimize loss
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001).minimize(loss)


start = time.time()
writer = tf.summary.FileWriter('./graphs/linear_reg', tf.get_default_graph())
with tf.Session() as sess:
    # Step 7: initialize the necessary variables, in this case, w and b
    sess.run(tf.global_variables_initializer()) 

    # Step 8: train the model for 100 epochs
    for i in range(100): 
        total_loss = 0
        for x, y in data:
            # Session execute optimizer and fetch values of loss
            _, l = sess.run([optimizer, loss], feed_dict={X: x, Y:y}) 
            total_loss += l
        print('Epoch {0}: {1}'.format(i, total_loss/n_samples))

    # close the writer when you're done using it
    writer.close() 

    # Step 9: output the values of w and b
    w_out, b_out = sess.run([w, b]) 

print('Took: %f seconds' %(time.time() - start))
print(f'w = {w_out}')

# plot the results
plt.plot(data[:,0], data[:,1], 'bo', label='Real data')
plt.plot(data[:,0], data[:,0] * w_out + b_out, 'r', label='Predicted data')
plt.legend()
plt.show()

对于我丢失的线性预测器(这与讲义一致):

Epoch 99: 30.03552558278714

对于我在二次方的尝试,我失去了:

Epoch 99: 127.2992221294363

【问题讨论】:

  • 无法从我的电脑打开链接。请在问题中发布相关代码。

标签: tensorflow


【解决方案1】:
  1. 在您链接的代码中,它是 100 个 epoch,每批 1 个(假设 data 的每个元素都是一个输入)。 IE。计算单个示例的损失梯度,更新参数,转到下一个示例……直到遍历整个数据集。这样做 100 次。
  2. 很多事情发生在优化器的minimize 调用中。实际上,您只需输入成本:在后台,Tensorflow 将为所有涉及成本计算的请求变量计算梯度(我们将在一秒钟内完成)(它可以从计算图中推断出这一点)并返回一个“应用”渐变的操作。这意味着一个操作会获取所有请求的变量并为其分配一个新值,例如tf.assign(var, var - learning_rate*gradient)。这与您提出的另一个问题有关:minimize 仅返回一个操作,这没有任何作用! 在会话中运行此操作每次都会执行“渐变步骤”。

至于哪些变量实际上受此操作影响:您可以将其作为minimize 调用的参数! See here -- 参数是var_list。如果没有给出,Tensorflow 将简单地使用所有“可训练变量”。默认情况下,您使用tf.Variabletf.get_variable 创建的任何变量都是可训练的。但是,您可以将trainable=False 传递给这些函数以创建(默认情况下)将受minimize 返回的操作影响的变量。玩这个!看看如果您将一些变量设置为不可训练,或者如果您将自定义 var_list 传递给 minimize 会发生什么。
一般来说,Tensorflow 的“整体思想”是它可以“神奇地”根据模型的前馈描述计算梯度。
编辑:这是可能的,因为机器学习模型(包括深度学习)由非常简单的构建块组成,例如矩阵乘法和主要是逐点非线性。这些简单的块也有简单的导数,可以通过链式规则组成。您可能需要阅读反向传播算法。
非常大的模型肯定需要更长的时间。但是,只要在所有组件都定义了导数的计算图中有一条清晰的“路径”,它总是可能的。
至于这是否会产生糟糕的模型:是的,这是深度学习的一个基本问题。非常复杂/深度的模型会导致高度非凸的成本函数,难以用梯度下降等方法进行优化。

关于二次函数:看起来这里有两个问题。

  1. 没有足够的训练周期。更复杂的问题(在这种情况下,我们有更多的变量)可能只需要更长的时间来训练。例如。使用您的设置,在使用二次函数大约 330 个 epoch 后,我可以达到 ~58 的成本。
  2. 学习率。以上仍然是可疑的,因为有了更多的变量,我们肯定能够得到更好的结果(只要这些变量的输入不是多余的),而且由于这是一个简单的线性回归问题,梯度下降应该能够找到它们.在这种情况下,学习率通常是问题所在。我将其更改为 0.0001(降低了 10 倍),并且在大约 3400 个 epoch 达到成本低于 30 之后(尚未测试它有多低)。现在显然较低的学习率会导致较慢的训练,但它们通常在最后是必要的,以避免“跳过”更好的解决方案。这就是为什么在实践中,通常会执行某种学习率退火——从一个大的学习率开始,以便在开始时取得快速进展,然后随着训练的进行越来越小。一般来说,学习率(及其退火时间表)是机器学习问题中最需要调整的超参数。
    还有一些方法,例如Adam,使用“自适应”学习率。通常,未调整的自适应方法将优于未调整的梯度下降,因此它们适合快速实验。然而,经过良好调整的梯度下降通常会反过来胜过它们。

【讨论】:

  • 哇 - 谢谢(再次)。我的心有点炸了。我在计算中看到的最接近巫术的东西。从经验上讲,你能介意说明当你开始进行极其复杂的计算以“最小化”时会发生什么 - 它是否开始花费极长的时间来计算和/或生成糟糕的模型?它会不会抛出运行时错误(即在某些时候它说抱歉,但这对我来说太混乱了,无法分析)?
  • 我已经更新了原始的 qn 以显示我尝试更改为二次形式
  • 酷,我更新了我的答案以解决二次问题/您的评论。
  • 感谢您澄清这一点。知道这是超参数的问题而不是错误实现的代码对我来说非常有用。您关于学习率的 cmets 很有启发性。
  • 这个答案值得奥斯卡!
猜你喜欢
  • 2017-05-09
  • 2014-09-15
  • 2018-02-06
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多