在这个例子中,TensowFlow GradientDescentOptimizer做了什么?

时间:2018-03-22 17:00:22

标签: tensorflow

我试图做Stanfords CS20: TensorFlow for Deep Learning Research课程。前两个讲座提供了对低级管道和计算框架的一个很好的介绍(坦率地说,官方的入门教程似乎跳过了原因我只能理解为虐待狂)。在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次)

据我所知,在实践中你应用了批量和子集这样的东西,但在这种情况下,我相信这只是在整个数据集上循环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

1 个答案:

答案 0 :(得分:2)

  1. 在您链接的代码中,它是1个批次的100个纪元(假设data的每个元素是单个输入)。即根据单个示例计算损失的梯度,更新参数,转到下一个示例...直到您遍历整个数据集。这样做100次。
  2. 在优化程序的minimize调用中发生了批次事件。实际上,您只需付出代价:在引擎盖下,Tensorflow将计算所有请求变量的梯度(我们将在一秒钟内得到它),这些变量涉及成本计算(它可以从计算图中推断出来)并返回一个“应用”渐变的操作。这意味着一个op,它接受所有请求的变量并为它们分配一个新值,如tf.assign(var, var - learning_rate*gradient)。这与您提出的另一个问题有关:minimize只返回一个操作,这不会做任何事情! 在会话中运行此操作将每次执行“渐变步骤”。
  3. 关于哪个变量实际受此操作影响:您可以将此作为minimize调用的参数! See here - 参数为var_list。如果没有给出,Tensorflow将简单地使用所有“可训练变量”。默认情况下,您使用tf.Variabletf.get_variable创建的任何变量都是可训练的。但是,您可以将trainable=False传递给这些函数,以创建(默认情况下)的变量,这些变量将受minimize返回的操作的影响。玩这个!如果您将某些变量设置为无法训练,或者将自定义var_list传递给minimize,请查看会发生什么。 一般来说,Tensorflow的“整体想法”是它可以“神奇地”仅基于模型的前馈描述来计算渐变。
    编辑:这是可能的,因为机器学习模型(包括深度学习)由非常简单的构建块组成,例如矩阵乘法和大多数逐点非线性。这些简单的块也有简单的导数,可以通过链规则组成。您可能想要阅读反向传播算法 非常大的模型肯定需要更长的时间。但只要在计算图中有一条明确的“路径”,所有组件都有定义的导数,它总是可能的 至于这是否会产生不良模型:是的,这是深度学习的基本问题。非常复杂/深的模型导致高度非凸的成本函数,难以用梯度下降等方法进行优化。

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

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