[翻译]斯坦福CS 20SI:基于Tensorflow的深度学习研究课程笔记,Lecture note 3: Linear and Logistic Regression in TensorFlow

“CS 20SI: TensorFlow for Deep Learning Research” Prepared by Chip Huyen
Reviewed by Danijar Hafner

Lecture note 3: Linear and Logistic Regression in TensorFlow
个人翻译,部分内容较简略,建议参考原note阅读

1. tensorflow的线性回归

我们经常听说保险公司使用例如一个社区的火灾和盗贼去衡量一个社区的安全程度,我的问题是,这是不是多余的,火灾和盗贼在一个社区里是否是相关的,如果相关,那么我们能不能找到他们的关系

换句话说,我们能不能找到一个函数f,如果X是火灾数并且Y是盗贼数,是否存在Y=f(X)?

给出这个关系,如果我们有特定社区的火灾数我们能不能预测这一区域的盗贼数

我们有 the U.S. Commission on Civil Rights, courtesy of C engage Learning 的一个数据集

数据集描述:
名称:芝加哥的火灾和盗贼
X=每1000住房单元的活仔数
Y=每1000人口的盗贼数
每对数据取自地区码不同的芝加哥的42个区域

方案:
首先假设火灾数和盗贼数成线性关系:Y=wX+b
我们需要找到参数w和b,通过平方差误差作为的损失函数,写出如下程序:

"""
Simple linear regression example in TensorFlow
This program tries to predict the number of thefts from 
the number of fire in the city of Chicago
"""

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

DATA_FILE = 'data/fire_theft.xls'

# Step 1: read in data from the .xls file
book = xlrd.open_workbook(DATA_FILE, encoding_override="utf-8")
sheet = book.sheet_by_index(0)
data = np.asarray([sheet.row_values(i) for i in range(1, sheet.nrows)])
n_samples = sheet.nrows - 1

# Step 2: create placeholders for input X (number of fire) and label Y (number of theft)
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.Variable(0.0, name='weights')
b = tf.Variable(0.0, name='bias')

# Step 4: build model to predict Y
Y_predicted = X * w + 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()) 

    writer = tf.summary.FileWriter('./my_graph/03/linear_reg', sess.graph)

    # Step 8: train the model
    for i in range(100): # train the model 100 times
        total_loss = 0
        for x, y in data:
            # Session runs train_op 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_value, b_value = sess.run([w, b]) 

# plot the results
X, Y = data.T[0], data.T[1]
plt.plot(X, Y, 'bo', label='Real data')
plt.plot(X, X * w_value + b_value, 'r', label='Predicted data')
plt.legend()
plt.show()

训练100遍后我们得到了平均平方误差为1372.77701716,w = 1.62071,b = 16.9162,误差太大了
这里写图片描述

拟合情况并不好,我们尝试使用二次函数Y=wXX+uX+b,我们只需要添加另外一个变量u并且修改Y_predicted公式

# Step 3: create variables: weights_1, weights_2, bias. All are initialized to 0
w = tf.Variable(0.0, name = "weights_1") 
u = tf.Variable(0.0 , name="weights_2") 
b = tf.Variable(0.0 , name = "bias")
# Step 4: predict Y (number of theft) from the number of fire
Y_predicted = X * X * w + X * u + b 
# Step 5: Profit!

10遍训练后,我们得到了平方平均损失797.335975976 , w, u, b = [0.071343 0.010234 0.00143057]
这里写图片描述

相比于线性函数花费了更少的时间收敛,但是由于由于最右几个偏差比较大的点还是没有很好的拟合,使用Huber loss代替MSE或者三次函数作为拟合函数可能会更好,你可以自己试一试
这里写图片描述

我怎么知道我的模型是正确的?

  1. 使用相关系数R-squared
    如果你不知道R-squared是什么,Minitab有一个博客解释.

下面是R-squared的要点:
- R-squared是一种关于数据拟合回归线的统计上的测量方法,它也被称为拟合优度,或者是多元回归的多元拟合优度。
- R-squared的解释为合理且简单的:他是反应变量与线性模型变化的比,R-squared=解释变异差(Explained variation)/ 总变分(Total variation)

  1. 在测试集中运行
    很多论文提出了没有足够备用数据集时的测试方法,比如k-fold交叉验证等。

  2. 使用伪造数据测试模型
    我们可以伪造一些呈已知现线性关系的数据去测试模型,,我们伪造100个数据点(X,Y)使得Y~3*X,并且查看我们的模型是否会输出w=3,b=0

生成伪造数据:

#each value y is approximately linear but with some random noise
X_input = np.linspace(- 1,  1 , 100)
Y_input = X_input * 3 + np.random.randn(X_input.shape [ 0 ]) * 0.5

这里写图片描述

分析代码

我们的模型代码非常简单易懂,除了两行:

optimizer  =  tf.train.GradientDescentOptimizer(learning_rate = 0.01 ).minimize(loss)
sess.run(optimizer,feed_dict ={ X:x , Y:y })

我记得我第一次看到类似这样的代码非常困惑,两个问题:
1. 为什么train_op在tf.Session.run()的fetch列表中?
2. tf怎么知道更新哪个变量?

我们实际上可以用tf.session.run()传递feed_dict给任何tf节点,tf将会执行被依赖的部分图,所以我们看到train_op的目的是最小化loss,loss依赖于变量w和b

这里写图片描述

从图中可知giant节点GrandientDescentOptimizer依赖于3个节点,weight,bias,和gradients(全部自动处理)

优化

GrandientDescentOptimizer意味着我们的更新规则是梯度下降,tf自动完成:更新w和b的值并且最小化loss,自动求梯度让人震惊!

优化训练默认训练所有目标函数以来的变量,如果有你不想要训练的变量,你可以在声明时把他们的关键字trainable设定为False 比如变量global_step,一个你会在很多tf模型中看到的用来保存模型运行次数的变量

global_step = tf.Variable(0, trainable=False, dtype=tf.int32)
learning_rate = 0.01 * 0.99 ** tf.cast(global_step, tf.float32)
increment_step = global_step.assign_add(1)
optimizer = tf.GradientDescentOptimizer(learning_rate) # learning rate can be a
tensor

类tf.variable的完整定义:

tf.Variable(initial_value=None,trainable=True,collections=None,validate_shap=True,caching_device=None,name=None,variable_def=None,type=None,expected_shape=None,import_scope = None)

你也可以使你的optimizer计算具体变量的梯度,你也可以修改成用自己的optimizer计算梯度

# create an optimizer.
optimizer = GradientDescentOptimizer(learning_rate = 0.1) # compute the gradients for a list of variables.
grads_and_vars = opt.compute_gradients(loss, <list of variables>)
# grads_and_vars is a list of tuples (gradient, variable). Do whatever you # need to the 'gradient' part, for example, subtract each of them by 1. 
subtracted_grads_and_vars =  [(gv[0] - 1.0,gv[1]) for gv in grads_and_vars]
#ask the optimizer to apply the subtracted gradients.
optimizer.apply_gradients(subtracted_grads_and_vars)

更多计算梯度的方法

optimizer类自动计算图中节点的导数,但是新optimizer的创建者或者熟练地使用者使用如下的更低级的函数:

tf.gradients(ys,xs,grad_ys=None,name='gradients',colocate_gradients_with_ops=False,gate_gradients=False,aggregation_method=None)

ys和xs为张量列表,这个方法构造当y为ys,和x为xs时各自的偏导列表,列表和ys长度相同

  • 技术细节:在训练模型的一部分是这个方法尤其的有用,比如,我们可以使用tf,gradients()获得中间层关于loss的导数G,然后然后我们使用optimizer来最小化中间层输出M和M + G之间的差,仅仅更新神经网络的靠近输入层的半个区域

optimizer列表

GradientDescentOptimizer并不是tf支持的唯一的更新规则,下面是截止到1/8/2017 tf支持的optimizer的列表,你可以通过官方文档了解更多:
- tf.train.GradientDescentOptimizer
- tf.train.AdadeltaOptimizer
- tf.train.AdagradDAOptimizer
- tf.train.MomentumOptimizer
- tf.train.AdamOptimizer
- tf.train.FtrlOptimizer
- tf.train.ProximalGradientDescentOptimizer
- tf.train.ProximalAdagradOptimizer
- tf.train.RMSPropOptimizer

Sebastian Ruder在他的博客做了这些optimizer的比较,如果你懒得读他的博客,这有一份总结:
“RMSprop方法是Adagrad方法的一个扩展,它从根本上解决了学习率降低。它与Adadelta相同,除了Adadelta方法在参数更新中更新中使用了RMS方法的numerator规则。adam,最终向RMSprop添加了偏差校正和动量。至少RMSprop,Adadelta和Adam是非常类似的算法,在类似的情况下表现良好。 Kingma等人证明偏差校正帮助Adam方法在梯度稀疏时略微优于RMSprop方法。至少,Adam可能总体上是选择。?

总结:使用adamOptimizer

tensorflow实现的逻辑回归

提到线性回归就不能不提到逻辑回归,我们可以使用逻辑回归解决一个很老的问题:MNIST数据集下的分类问题

MNIST可能是最受欢迎的数据集之一,被用于各种图像处理模型,他是一个首页数字的数据集:
这里写图片描述
每个图片28*28像素,拉伸为1维张量长度为784,每一个都有一个标签,比如第一行标签为0,第二行为1……
TFLearn(tf的一个简单接口)有一个让你可以从Yan Lecun个人网站加载MNIST数据集的脚本,并且把它分为训练集,验证集和测试集

from tensorflow.examples.tutorials.mnist import input_data 
MNIST  =  input_data.read_data_sets("/data/mnist",one_hot = True )

One-hot编码:
在数字电路中,One-hot是只有一位为1其他为0的一组bit数组,所以输出图像是数字7时,输出应该被编码为10位数组,其中只有第八位为1

MNIST是一个tf数据及对象,训练集有55000个数据(MNIST.train),测试集有10000个数据(MNIST.test),验证集有5000个数据(MNIST.validation)

逻辑回归模型和线性回归模型十分相似,然而,现在我有了更多的数据,我们学习过CS229知道如果我们计算每个单独的数据的梯度将会是非常的慢的,一种方法是使用批梯度下降(batch gradient decent),幸运的是tf有一个非常好的批数据支持

建立批逻辑回归,我们只需要改变X_placeholder和Y_placeholder的尺寸,可以容纳batch_size数据的尺寸

X  =  tf.placeholder(tf.float32, [batch_size, 784 ],name = "image" ) 
Y  =  tf.placeholder(tf.float32, [batch_size,  10 ],name = "label")

这里有完整的实现:

"""
Simple logistic regression model to solve OCR task 
with MNIST in TensorFlow
MNIST dataset: yann.lecun.com/exdb/mnist/
"""

import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
import time
# Define paramaters for the model
learning_rate = 0.01
batch_size = 128
n_epochs = 30

# Step 1: Read in data
# using TF Learn's built in function to load MNIST data to the folder data/mnist
mnist = input_data.read_data_sets('/data/mnist', one_hot=True) 

# Step 2: create placeholders for features and labels
# each image in the MNIST data is of shape 28*28 = 784
# therefore, each image is represented with a 1x784 tensor
# there are 10 classes for each image, corresponding to digits 0 - 9. 
# each lable is one hot vector.
X = tf.placeholder(tf.float32, [batch_size, 784], name='X_placeholder') 
Y = tf.placeholder(tf.float32, [batch_size, 10], name='Y_placeholder')

# Step 3: create weights and bias
# w is initialized to random variables with mean of 0, stddev of 0.01
# b is initialized to 0
# shape of w depends on the dimension of X and Y so that Y = tf.matmul(X, w)
# shape of b depends on Y
w = tf.Variable(tf.random_normal(shape=[784, 10], stddev=0.01), name='weights')
b = tf.Variable(tf.zeros([1, 10]), name="bias")

# Step 4: build model
# the model that returns the logits.
# this logits will be later passed through softmax layer
logits = tf.matmul(X, w) + b 

# Step 5: define loss function
# use cross entropy of softmax of logits as the loss function
entropy = tf.nn.softmax_cross_entropy_with_logits(logits, Y, name='loss')
loss = tf.reduce_mean(entropy) # computes the mean over all the examples in the batch

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

with tf.Session() as sess:
    # to visualize using TensorBoard
    writer = tf.summary.FileWriter('./my_graph/03/logistic_reg', sess.graph)

    start_time = time.time()
    sess.run(tf.global_variables_initializer()) 
    n_batches = int(mnist.train.num_examples/batch_size)
    for i in range(n_epochs): # train the model n_epochs times
        total_loss = 0

        for _ in range(n_batches):
            X_batch, Y_batch = mnist.train.next_batch(batch_size)
            _, loss_batch = sess.run([optimizer, loss], feed_dict={X: X_batch, Y:Y_batch}) 
            total_loss += loss_batch
        print 'Average loss epoch {0}: {1}'.format(i, total_loss/n_batches)

    print 'Total time: {0} seconds'.format(time.time() - start_time)

    print('Optimization Finished!') # should be around 0.35 after 25 epochs

    # test the model
    n_batches = int(mnist.test.num_examples/batch_size)
    total_correct_preds = 0
    for i in range(n_batches):
        X_batch, Y_batch = mnist.test.next_batch(batch_size)
        _, loss_batch, logits_batch = sess.run([optimizer, loss, logits], feed_dict={X: X_batch, Y:Y_batch}) 
        preds = tf.nn.softmax(logits_batch)
        correct_preds = tf.equal(tf.argmax(preds, 1), tf.argmax(Y_batch, 1))
        accuracy = tf.reduce_sum(tf.cast(correct_preds, tf.float32)) # need numpy.count_nonzero(boolarr) :(
        total_correct_preds += sess.run(accuracy)   

    print 'Accuracy {0}'.format(total_correct_preds/mnist.test.num_examples)

    writer.close()

在我的mac上,batch版本的模型运行一次需要0.5秒,无batch的模型运行一次需要24秒,然而当你增加batch大小时,你需要更多次epoch(全部数据集)次数,因为更大的batch大小会使运行一次epoch的次数变少,mini-batch size” in Bengio’s practical tips .有详细论述。

我们可以使用测试集测试模型:

#test the model
  n_batches =  int(MNIST.test.num_examples/batch_size) 
  total_correct_preds=0
  for i in range(n_batches):
    X_batch, Y_batch = MNIST.test.next_batch(batch_size)
    _ , loss_batch , logits_batch = sess.run([optimizer , loss, logits], feed_dict ={X:X_batch,Y:Y_batch })
  preds = tf.nn.softmax(logits_batch)
  correct_preds = tf.equal(tf.argmax(preds , 1 ), tf . argmax(Y_batch , 1)) 
  accuracy = tf.reduce_sum(tf.cast(correct_preds,tf.float32 ))   # similar

训练十个epoch后,我们的模型达到了90%的准确率

注意:tf有给MNIST数据集的feeder,但是不要依赖于他,你应该学会如何写自己的数据分析程序。

graph在tensorboard中:
这里写图片描述

下节课我们将学习如何构造自己的模型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值