使用TensorFlow进行线性回归

6

我想理解线性回归...这是我试图理解的脚本:

'''
A linear regression learning algorithm example using TensorFlow library.
Author: Aymeric Damien
Project: https://github.com/aymericdamien/TensorFlow-Examples/
'''

from __future__ import print_function

import tensorflow as tf
from numpy import *
import numpy
import matplotlib.pyplot as plt
rng = numpy.random

# Parameters
learning_rate = 0.0001
training_epochs = 1000
display_step = 50

# Training Data
train_X = numpy.asarray([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,
                         7.042,10.791,5.313,7.997,5.654,9.27,3.1])
train_Y = numpy.asarray([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53,1.221,
                         2.827,3.465,1.65,2.904,2.42,2.94,1.3])

train_X=numpy.asarray(train_X)
train_Y=numpy.asarray(train_Y)
n_samples = train_X.shape[0]


# tf Graph Input
X = tf.placeholder("float")
Y = tf.placeholder("float")

# Set model weights
W = tf.Variable(rng.randn(), name="weight")
b = tf.Variable(rng.randn(), name="bias")

# Construct a linear model
pred = tf.add(tf.multiply(X, W), b)


# Mean squared error
cost = tf.reduce_sum(tf.pow(pred-Y, 2))/(2*n_samples)
# Gradient descent
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)

# Initializing the variables
init = tf.global_variables_initializer()

# Launch the graph
with tf.Session() as sess:
    sess.run(init)

    # Fit all training data
    for epoch in range(training_epochs):
        for (x, y) in zip(train_X, train_Y):
            sess.run(optimizer, feed_dict={X: x, Y: y})

        # Display logs per epoch step
        if (epoch+1) % display_step == 0:
            c = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
            print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(c), \
                "W=", sess.run(W), "b=", sess.run(b))

    print("Optimization Finished!")
    training_cost = sess.run(cost, feed_dict={X: train_X, Y: train_Y})
    print("Training cost=", training_cost, "W=", sess.run(W), "b=", sess.run(b), '\n')

    # Graphic display
    plt.plot(train_X, train_Y, 'ro', label='Original data')
    plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
    plt.legend()
    plt.show()

问题是这部分代表什么:
# Set model weights
W = tf.Variable(rng.randn(), name="weight")
b = tf.Variable(rng.randn(), name="bias")

为什么会出现随机的浮点数?

另外,您能否用符号表示成本、预测、优化器变量的某些数学公式给我看一下?

3个回答

10

让我们尝试将一些直觉和来源与tf方法结合起来。

总体直觉:

这里介绍的回归是一个监督学习问题。如Russel&Norvig的人工智能中所定义的那样,任务是:

给定一个由m个输入输出对(x1, y1), (x2, y2), ... , (xm, ym)组成的训练集(X, y),其中每个输出都是由未知函数y = f(x)生成的,发现一个函数h,它近似于真实函数f

为此,h 假设函数以某种方式将每个x与待学习的参数相结合,以便输出尽可能接近相应的y,并且这适用于整个数据集。希望得到的函数将接近于f

但是如何学习这些参数呢? 为了能够学习,模型必须能够评估。这里就要用到成本(也称为损失能量优点等...)函数了:它是一个度量函数,比较h的输出与相应的y,并惩罚大的差异

现在应该清楚了学习过程的确切含义:改变参数以实现成本函数的更低值

线性回归:

您发布的示例执行参数线性回归,使用以平均平方误差作为成本函数的梯度下降进行优化。这意味着:

  • 参数化:参数集是固定的。在整个学习过程中,它们都被保存在完全相同的内存占位符中。

  • 线性:函数h的输出仅是输入x和参数之间的线性(实际上是仿射)组合。因此,如果xw是具有相同维度的实值向量,b是实数,则有h(x,w, b)= w.transposed()*x+bDeep Learning Book第107页提供了更多关于这方面的质量见解和直觉。

  • 代价函数:现在这是有趣的部分。平均平方误差是一个函数。这意味着它有一个单一的全局最优解,并且可以直接使用一组正常方程式找到(也在DLB中解释)。在您的示例中,使用随机(和/或小批量)梯度下降法:这是优化非凸代价函数(在更高级的模型如神经网络中的情况)或当数据集具有巨大维度时的首选方法(也在DLB中解释)。

  • 梯度下降tf已经为您处理了这个问题,因此只需说GD通过跟随其导数“向下”以小步骤最小化代价函数,直到达到一个鞍点。如果您完全需要知道,TF应用的确切技术称为自动微分,这是数值和符号方法之间的一种折衷方案。对于像您这样的凸函数,这个点将是全局最优解,并且(如果您的学习率不太大)它将始终收敛到该点,因此不管您使用哪些值初始化变量。在更复杂的体系结构(如神经网络)中,随机初始化是必要的。关于小批量的管理还有一些额外的代码,但我不会深入讨论,因为这不是您问题的主要焦点。

TensorFlow的方法:

深度学习框架现在主要是通过构建计算图来嵌套大量函数(您可能想看一下我几周前关于DL框架的演示)。为了构建和运行图形,TensoFlow遵循声明式风格,这意味着必须先完全定义和编译图形,然后再部署和执行。如果您还没有阅读过这篇简短的维基文章,非常推荐您阅读。在这种情况下,设置分为两个部分:

  1. 首先,您需要定义计算图形,在其中将数据集和参数放入内存占位符中,构建假设和成本函数,并告诉tf应用哪种优化技术。

  2. 然后,在会话中运行计算,库将能够(重新)加载数据占位符并执行优化。

代码:

示例代码紧密遵循此方法:

  1. 定义测试数据 X 和标签 Y,并为它们在图中准备一个占位符(在 feed_dict 部分中提供)。

  2. 为参数定义 'W' 和 'b' 占位符。它们必须是 变量,因为它们将在会话期间更新。

  3. 按照之前的说明定义 pred(我们的假设)和 cost


从这里开始,代码的其余部分应该更加清晰。关于优化器,正如我所说,tf已经知道如何处理它,但您可能需要查看梯度下降以获取更多细节(同样,DLB是一个非常好的参考)

干杯! 安德烈斯


代码示例:梯度下降与正规方程

这些小片段生成简单的多维数据集并测试两种方法。请注意,正规方程方法不需要循环,并带来更好的结果。对于小维数(DIMENSIONS<30k),这可能是首选方法:

from __future__ import absolute_import, division, print_function
import numpy as np
import tensorflow as tf

####################################################################################################
### GLOBALS
####################################################################################################
DIMENSIONS = 5
f = lambda(x): sum(x) # the "true" function: f = 0 + 1*x1 + 1*x2 + 1*x3 ...
noise = lambda: np.random.normal(0,10) # some noise

####################################################################################################
### GRADIENT DESCENT APPROACH
####################################################################################################
# dataset globals
DS_SIZE = 5000
TRAIN_RATIO = 0.6 # 60% of the dataset is used for training
_train_size = int(DS_SIZE*TRAIN_RATIO)
_test_size = DS_SIZE - _train_size
ALPHA = 1e-8 # learning rate
LAMBDA = 0.5 # L2 regularization factor
TRAINING_STEPS = 1000

# generate the dataset, the labels and split into train/test
ds = [[np.random.rand()*1000 for d in range(DIMENSIONS)] for _ in range(DS_SIZE)] # synthesize data
# ds = normalize_data(ds)
ds = [(x, [f(x)+noise()]) for x in ds] # add labels
np.random.shuffle(ds)
train_data, train_labels = zip(*ds[0:_train_size])
test_data, test_labels = zip(*ds[_train_size:])

# define the computational graph
graph = tf.Graph()
with graph.as_default():
  # declare graph inputs
  x_train = tf.placeholder(tf.float32, shape=(_train_size, DIMENSIONS))
  y_train = tf.placeholder(tf.float32, shape=(_train_size, 1))
  x_test = tf.placeholder(tf.float32, shape=(_test_size, DIMENSIONS))
  y_test = tf.placeholder(tf.float32, shape=(_test_size, 1))
  theta = tf.Variable([[0.0] for _ in range(DIMENSIONS)])
  theta_0 = tf.Variable([[0.0]]) # don't forget the bias term!
  # forward propagation
  train_prediction = tf.matmul(x_train, theta)+theta_0
  test_prediction  = tf.matmul(x_test, theta) +theta_0
  # cost function and optimizer
  train_cost = (tf.nn.l2_loss(train_prediction - y_train)+LAMBDA*tf.nn.l2_loss(theta))/float(_train_size)
  optimizer = tf.train.GradientDescentOptimizer(ALPHA).minimize(train_cost)
  # test results
  test_cost = (tf.nn.l2_loss(test_prediction - y_test)+LAMBDA*tf.nn.l2_loss(theta))/float(_test_size)

# run the computation
with tf.Session(graph=graph) as s:
  tf.initialize_all_variables().run()
  print("initialized"); print(theta.eval())
  for step in range(TRAINING_STEPS):
    _, train_c, test_c = s.run([optimizer, train_cost, test_cost],
                               feed_dict={x_train: train_data, y_train: train_labels,
                                          x_test: test_data, y_test: test_labels })
    if (step%100==0):
      # it should return bias close to zero and parameters all close to 1 (see definition of f)
      print("\nAfter", step, "iterations:")
      #print("   Bias =", theta_0.eval(), ", Weights = ", theta.eval())
      print("   train cost =", train_c); print("   test cost =", test_c)
  PARAMETERS_GRADDESC = tf.concat(0, [theta_0, theta]).eval()
  print("Solution for parameters:\n", PARAMETERS_GRADDESC)

####################################################################################################
### NORMAL EQUATIONS APPROACH
####################################################################################################
# dataset globals
DIMENSIONS = 5
DS_SIZE = 5000
TRAIN_RATIO = 0.6 # 60% of the dataset isused for training
_train_size = int(DS_SIZE*TRAIN_RATIO)
_test_size = DS_SIZE - _train_size
f = lambda(x): sum(x) # the "true" function: f = 0 + 1*x1 + 1*x2 + 1*x3 ...
noise = lambda: np.random.normal(0,10) # some noise
# training globals
LAMBDA = 1e6 # L2 regularization factor

# generate the dataset, the labels and split into train/test
ds = [[np.random.rand()*1000 for d in range(DIMENSIONS)] for _ in range(DS_SIZE)]
ds = [([1]+x, [f(x)+noise()]) for x in ds] # add x[0]=1 dimension and labels
np.random.shuffle(ds)
train_data, train_labels = zip(*ds[0:_train_size])
test_data, test_labels = zip(*ds[_train_size:])

# define the computational graph
graph = tf.Graph()
with graph.as_default():
  # declare graph inputs
  x_train = tf.placeholder(tf.float32, shape=(_train_size, DIMENSIONS+1))
  y_train = tf.placeholder(tf.float32, shape=(_train_size, 1))
  theta = tf.Variable([[0.0] for _ in range(DIMENSIONS+1)]) # implicit bias!
  # optimum
  optimum = tf.matrix_solve_ls(x_train, y_train, LAMBDA, fast=True)

# run the computation: no loop needed!
with tf.Session(graph=graph) as s:
  tf.initialize_all_variables().run()
  print("initialized")
  opt = s.run(optimum, feed_dict={x_train:train_data, y_train:train_labels})
  PARAMETERS_NORMEQ = opt
  print("Solution for parameters:\n",PARAMETERS_NORMEQ)

####################################################################################################
### PREDICTION AND ERROR RATE
####################################################################################################

# generate test dataset
ds = [[np.random.rand()*1000 for d in range(DIMENSIONS)] for _ in range(DS_SIZE)]
ds = [([1]+x, [f(x)+noise()]) for x in ds] # add x[0]=1 dimension and labels
test_data, test_labels = zip(*ds)
# define hypothesis
h_gd = lambda(x): PARAMETERS_GRADDESC.T.dot(x)
h_ne = lambda(x): PARAMETERS_NORMEQ.T.dot(x)
# define cost
mse = lambda pred, lab: ((pred-np.array(lab))**2).sum()/DS_SIZE
# make predictions!
predictions_gd = np.array([h_gd(x) for x in test_data])
predictions_ne = np.array([h_ne(x) for x in test_data])
# calculate and print total error
cost_gd = mse(predictions_gd, test_labels)
cost_ne = mse(predictions_ne, test_labels)
print("total cost with gradient descent:", cost_gd)
print("total cost with normal equations:", cost_ne)

请看这张图片:https://raw.githubusercontent.com/ritchieng/machine-learning-stanford/master/w1_linear_regression_one_variable/2_params.png - fr_andres
它代表一个成本函数,其中theta0是你的b,theta1是你的(一维)W。由于其凸性,无论你从哪里开始都没有关系!梯度下降就像把球扔进碗里:它会落到全局最小值。在你的情况下,它略高于56。如果你想要更精确(但速度较慢)的结果,请尝试降低学习率并增加迭代次数。如果你想要精确的解决方案,可以尝试使用正规方程方法:https://www.tensorflow.org/versions/r1.1/api_docs/python/tf/matrix - fr_andres
啊哈,对于需要进行预测的地方,我需要添加哪一行代码? - Vladimir Djukic
在打印语句下添加此行 w_opt, b_opt = W.eval(), b.eval(),然后您的预测函数应该可以工作:predict = lambda(x): w_opt*x+b_opt。顺便说一句,如果我运行代码,我会得到更低的成本:('Training cost=', 0.1288833, 'W=', 0.1152851, 'b=', 1.6928034) - fr_andres
是因为我从某个外部文件加载了数据... 我得到了不同的结果,第一次预测是:36.5961460665,第二次是:37.3570079133。这是什么原因? - Vladimir Djukic
显示剩余4条评论

0
变量允许我们向图形中添加可训练的参数。它们由类型和初始值构成:
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b

类型为 tf.Variable 的变量是我们将要用 TensorFlow 学习的参数。假设您使用了 梯度下降 来最小化损失函数。您需要先初始化这些参数。为此,可以使用 rng.randn() 生成一个随机值。

我认为 Getting Started With TensorFlow 是一个很好的起点。


0

我先定义变量:

W is a multidimensional line that spans R^d (same dimensionality as X)
b is a scalar value (bias) 
Y is also a scalar value i.e. the value at X

pred = W (dot) X + b   # dot here refers to dot product

# cost equals the average squared error
cost = ((pred - Y)^2) / 2*num_samples

#finally optimizer
# optimizer computes the gradient with respect to each variable and the update

W += learning_rate * (pred - Y)/num_samples * X 
b += learning_rate * (pred - Y)/num_samples 

为什么W和b被设置为随机值?因为它们会根据从成本计算中得出的梯度进行更新,所以W和b可以被初始化为任何值。虽然两种方法都会收敛于同一个解,但它并不是通过最小二乘法执行线性回归。

点击这里获取更多信息:入门指南


但是它们为什么一开始就被定义和随机呢?我怎样才能从得到的结果中进行预测呢? - Vladimir Djukic
我不确定你的意思是什么,“为什么要定义它们”。线性回归的整个目的就是建立一个线性预测器,即代表将X映射到Y的函数的直线。要进行预测,只需将新的X值插入占位符变量中即可。至于为什么它们是随机的,那么它们可以从任何值开始,为什么不能是随机值呢?也许您可以详细说明一下您的困惑。 - Steven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接