3.1 TensorFlow实战二:波士顿房价预测

【本文使用的是TensorFlow1.x,如需TensorFlow2.x的内容参见我的“TensorFlow2实战”笔记】

一、问题描述

波士顿平均房价即为标签值,它与12个特征变量相关。

源数据存储在一个名为“boston.csv”表格中,如下图所示。最后一列“MEDV”即标签值房价,其他12列为影响房价的特征。

二、回归模型

本例里,我们用最简单的线性回归(即特征为一次幂)来构建模型。由于有12个特征,所以是多元线性回归模型
y=w_{1}x_{1}+w_{2}x_{2}+\cdots+w_{n}x_{n}+b

简写为
y=\sum^{n}_{i=0}w_ix_i+b

或写为矩阵形式
y=\left[ \begin{matrix} x_1 & x_2 & \cdots &x_n \end{matrix} \right] \left[ \begin{matrix} w_1\\ w_2\\ \vdots\\ w_n \end{matrix} \right] + b

三、读取数据

1. 读取数据

加载所需库

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

其中,pandas用于读取和整理数据。用pandas读取源文件并显示摘要

#读取数据
df = pd.read_csv('boston.csv', header=0)
#显示摘要
print(df.describe())

输出

             CRIM         ZN      ...           LSTAT        MEDV
count  506.000000  506.000000     ...      506.000000  506.000000
mean     3.613524   11.363636     ...       12.653063   22.532806
std      8.601545   23.322453     ...        7.141062    9.197104
min      0.006320    0.000000     ...        1.730000    5.000000
25%      0.082045    0.000000     ...        6.950000   17.025000
50%      0.256510    0.000000     ...       11.360000   21.200000
75%      3.677082   12.500000     ...       16.955000   25.000000
max     88.976200  100.000000     ...       37.970000   50.000000

[8 rows x 13 columns]

也可以直接“print(df)”显示所有数据

         CRIM   ZN   INDUS   CHAS    NOX  ...   RAD  TAX  PTRATIO  LSTAT  MEDV
0     0.00632  18.0    2.31     0  0.538  ...     1  296     15.3   4.98  24.0
1     0.02731   0.0    7.07     0  0.469  ...     2  242     17.8   9.14  21.6
2     0.02729   0.0    7.07     0  0.469  ...     2  242     17.8   4.03  34.7
..        ...   ...     ...   ...    ...  ...   ...  ...      ...    ...   ...
504   0.10959   0.0   11.93     0  0.573  ...     1  273     21.0   6.48  22.0
505   0.04741   0.0   11.93     0  0.573  ...     1  273     21.0   7.88  11.9

[506 rows x 13 columns]

数据太多会显示为“...”折叠
这是一个pandas的数据表格式,包含列明和行号等数据。我们需要提取其中的值来做数学计算

#提取数值
df = df.values

此时再print(df)打印结果为

[[6.3200e-03 1.8000e+01 2.3100e+00 ... 1.5300e+01 4.9800e+00 2.4000e+01]
 [2.7310e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 9.1400e+00 2.1600e+01]
 [2.7290e-02 0.0000e+00 7.0700e+00 ... 1.7800e+01 4.0300e+00 3.4700e+01]
 ...
 [6.0760e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 5.6400e+00 2.3900e+01]
 [1.0959e-01 0.0000e+00 1.1930e+01 ... 2.1000e+01 6.4800e+00 2.2000e+01]
 [4.7410e-02 0.0000e+00 1.1930e+01 ... 2.1000e+01 7.8800e+00 1.1900e+01]]

可见数值被保存到一个python二维数组里了。然后把它转换为numpy数组以方便使用numpy的API

df = np.array(df,np.float32)

2. 分离特征和标签

用数组切片分离特征和标签数据

x_data = df[:, :12] #0~11列为特征值
y_data = df[:, 12] #第12列为标签

可以查看它们的形状

print('x_data shape = ', x_data.shape)
print('y_data shape = ', y_data.shape)

输出

x_data shape =  (506, 12)
y_data shape =  (506,)

即x_data有506行12列,y_data是有506个元素的数组

3. 特征归一化

对于多特征问题,不同特征数值的量级是不同的。比如本例中,特征“一氧化氮浓度NOX”小于1,而特征“TAX”在200~800范围。如果直接训练,则会出现搜索震荡甚至“梯度爆炸”问题


解决的方法是把特征归一化,即每个特征的所有数据都除以它最大值与最小值差

这样所有特征都会比较均匀地分布在0~1范文内,可加速训练和防止梯度爆炸

代码实现为

#特征数据归一化normalization
for i in range(x_data.shape[1]):
    x_data[:,i] /= (x_data[:,i].max() - x_data[:,i].min())

注意本例的标签值只有一个(即标量),所以没必要归一化。

四、模型定义

1. 定义占位符

定义特征数据和标签数据的占位符

x = tf.placeholder(tf.float32, [None, 12], name = 'x') #12个特征值
y = tf.placeholder(tf.float32, [None, 1], name = 'y') #1个标签值

注意第二个参数是shape,shape的第一个维度写None的意思是行数是待定的,在实际feed时候有多少行就是多少行——这样在后面训练时候就比较灵活,可以单样本训练也可以批量训练。

2. 定义模型函数

上述“二、回归模型”中,讲模型写入矩阵时,w是有12个元素的列向量,即w的形状是[12,1]。b是一个标量。所以定义变量w和b如下

w = tf.Variable(tf.random_normal([12,1], stddev=0.01), name='w')
b = tf.Variable(1.0, name='b')

其中,w以标准差0.01的随机正态分布赋初值,b以定值0.1赋初值。(赋初值很有学问,这里不展开讲)
然后定义回归模型的函数

def model(x, w, b):
    return tf.matmul(x, w) + b

tf.matmul是矩阵乘法。就相当于前文的回归函数
y=\left[ \begin{matrix} x_1 & x_2 & \cdots &x_n \end{matrix} \right] \left[ \begin{matrix} w_1\\ w_2\\ \vdots\\ w_n \end{matrix} \right] + b

然后就可以定义前向计算节点,即预测值,为

pred = model(x, w, b)

3. 写入命名空间

我们可以把上面这几行代码写在一个命名空间(name scope)里,相当于把这段代码打包。好处是在查看计算图时,这段代码就会显示为一个子图,看上去更简洁。方法是写到上下文管理器“with tf.name_scope('Model'):”中即可,“Model”是自定义的空间名

#定义命名空间
with tf.name_scope('Model'):
    #定义被训练变量
    w=tf.Variable(tf.random_normal([12,1], stddev=0.1),name='w')
    b=tf.Variable(1.0, name='b')
    #定义模型
    def model(x, w, b):
        return tf.matmul(x, w) + b
    #定义前向节点
    pred = model(x, w, b)

注意写入命名空间这一步并不是必须的,只是为了计算图显示更简洁。

五、模型训练

1. 设置超参数

根据经验,设置迭代200轮,学习率0.01

train_epochs = 200 #迭代轮数
learning_rate = 0.01 #学习率

超参数是可以尝试调整的

2. 定义损失函数

使用简单的均方差(MSE)损失函数。也写入一个命名空间以打包入子图,并命名为“LossFunction”

with tf.name_scope('LossFunction'):
    loss_function = tf.reduce_mean(tf.pow(y-pred, 2)) #均方差MSE

如果最后查看子图应显示为


3. 选择优化器

TensorFlow提供了非常多的优化器,这里选用最简单的梯度下降优化器。输入学习率和损失函数

optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)

4. 迭代训练

使用上下文管理器with/as建立会话session,并初始化变量。使用随机小批量(Mini-batch)训练,即每轮从训练集中随机抽取10个样本参与训练。每轮训练结束,都计算至今的平均损失并记录

with tf.Session() as sess:
    #声明初始化变量操作
    init = tf.global_variables_initializer()
    #初始化变量
    sess.run(init)
    
    loss_sum=0
    loss_ave_rec=[]
    for epoch in range(train_epochs):
        batch=np.random.randint(0, x_data.shape[0], 10) #随机取出x_data中的10组样本做训练
        _, loss=sess.run([optimizer, loss_function], feed_dict={x:x_data[batch], y:y_data[batch]})

        loss_sum+=loss
        loss_ave_rec.append(loss_sum/(epoch+1))

训练完毕后,可绘制平均损失趋势图

plt.figure()
plt.plot(loss_ave_rec)

输出


可见平均损失逐渐趋于稳定,说明训练效果达到极限(未必是最好的,可能通过改变训练策略和调整超参数来优化)。
值得一提的是,loss越小并不一定结果越好,它只标明对现有标签拟合得好,但对新标签的预测可能并不好,即出现所谓“过拟合”(overfitting)现象。(解决方法这里不展开讲)

六、TensorBoard可视化

为了在TensorBoard中可视化计算图,对上一段“迭代计算”部分的代码添加一些内容。添加的内容注释“For TB”,修改后的代码如下

#迭代训练
with tf.Session() as sess:
    #声明初始化变量操作
    init = tf.global_variables_initializer()
    
    #For TB: 设置日志存储目录
    logdir='d:/log'
    #For TB: 创建记录损失值的操作,命名为loss,可在TensorBoard中SCALARS栏看到
    sum_loss_op=tf.summary.scalar('loss', loss_function)
    #For TB: 合并所有需要记录的摘要日志文件,以方便一次性写入
    merged = tf.summary.merge_all()
    
    #初始化变量
    sess.run(init)
    
    #For TB: 创建摘要writer,以写入计算图,可在TensorBoard中GRAPHS栏中看到
    writer=tf.summary.FileWriter(logdir, sess.graph)
    
    loss_sum=0
    loss_ave_rec=[]
    for epoch in range(train_epochs):
        batch=np.random.randint(0, x_data.shape[0], 10) #随机取出x_data中的10组样本做训练
        
        #运行会话
        #For TB: 添加运行节点sum_loss_op
        _, loss, summary_str=sess.run([optimizer, loss_function, sum_loss_op], feed_dict={x:x_data[batch], y:y_data[batch]})
        
        #For TB: 将损失值记录到日志文件
        writer.add_summary(summary_str, epoch)
        
        #记录平均损失
        loss_sum+=loss
        loss_ave_rec.append(loss_sum/(epoch+1))

运行成功后,可以在路径“d:/log”看到前缀“events.out.tfevents.”的文件。
打开Anaconda Prompt,输入命令

tensorboard --logdir=d:\log

返回

TensorBoard 1.13.1 at http://XXXX:6006 (Press CTRL+C to quit)

其中XXXX是你的计算机名。这时TensorBoard就在后台启动了。
然后打开浏览器,输入地址

localhost:6006

就能看到TensorBoad界面了:

SCALARS标签下可以看到每轮的loss值。因为记录的不是平均值所有看到波动很大

GRAPHS标签下可以看到运行图,双击节点可以看内部的计算流

附:完整代码

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

#读取数据
df = pd.read_csv('boston.csv') #默认header=0,即第0行是列名
#显示摘要
print(df.describe())
#print(df)

df=df.values #提取数值
#print(df)
df = np.array(df,np.float32) #转换为numpy数组

x_data = df[:, :12] #0~11列为特征值
y_data=df[:, 12] #第12列为标签

#特征数据归一化normalization
for i in range(x_data.shape[1]):
    x_data[:,i] /= (x_data[:,i].max() - x_data[:,i].min())

#显示x_data和y_data的形状
print('x_data shape = ', x_data.shape)
print('y_data shape = ', y_data.shape)

#模型定义
x = tf.placeholder(tf.float32, [None, 12], name = 'x') #12个特征值
y = tf.placeholder(tf.float32, [None, ], name = 'y') #1个标签值

#定义命名空间
with tf.name_scope('Model'):
    #定义被训练变量
    w=tf.Variable(tf.random_normal([12,1], stddev=0.1),name='w')
    b=tf.Variable(1.0, name='b')
    #定义模型
    def model(x, w, b):
        return tf.matmul(x, w) + b
    #定义前向节点
    pred = model(x, w, b)

#设置超参数
train_epochs = 200 #迭代轮数
learning_rate = 0.01 #学习率
#定义损失函数
with tf.name_scope('LossFunction'):
    loss_function = tf.reduce_mean(tf.pow(y-pred, 2)) #均方差MSE
#调用梯度下降优化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss_function)

#迭代训练
with tf.Session() as sess:
    #声明初始化变量操作
    init = tf.global_variables_initializer()
    
    #For TB: 设置日志存储目录
    logdir='d:/log'
    #For TB: 创建记录损失值的操作,命名为loss,可在TensorBoard中SCALARS栏看到
    sum_loss_op=tf.summary.scalar('loss', loss_function)
    #For TB: 合并所有需要记录的摘要日志文件,以方便一次性写入
    merged = tf.summary.merge_all()
    
    #初始化变量
    sess.run(init)
    
    #For TB: 创建摘要writer,以写入计算图,可在TensorBoard中GRAPHS栏中看到
    writer=tf.summary.FileWriter(logdir, sess.graph)
    
    loss_sum=0
    loss_ave_rec=[]
    for epoch in range(train_epochs):
        batch=np.random.randint(0, x_data.shape[0], 10) #随机取出x_data中的10组样本做训练
        
        #运行会话
        #For TB: 添加运行节点sum_loss_op
        _, loss, summary_str=sess.run([optimizer, loss_function, sum_loss_op], feed_dict={x:x_data[batch], y:y_data[batch]})
        
        #For TB: 将损失值记录到日志文件
        writer.add_summary(summary_str, epoch)
        
        #记录平均损失
        loss_sum+=loss
        loss_ave_rec.append(loss_sum/(epoch+1))

#绘制平均损失趋势图  
plt.figure()
plt.plot(loss_ave_rec)

Reference:
https://www.icourse163.org/learn/ZUCC-1206146808?tid=1450260446#/learn/content?type=detail&id=1214536575&cid=1218338010

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容

  • 机器学习术语表 本术语表中列出了一般的机器学习术语和 TensorFlow 专用术语的定义。 A A/B 测试 (...
    yalesaleng阅读 1,969评论 0 11
  • 本内容为Udacity课程波士顿房价预测项目,欢迎阅读,有错的地方请留言。仅参考不建议作为其他用途。 优达学城毕业...
    MrMiaow阅读 14,370评论 1 18
  • 本文编译自谷歌开发者机器学习术语表项目,介绍了该项目所有的术语与基本解释。 A 准确率(accuracy) 分类模...
    630d0109dd74阅读 2,006评论 0 1
  • A 准确率(accuracy) 分类模型预测准确的比例。在多类别分类中,准确率定义如下: 在二分类中,准确率定义为...
    630d0109dd74阅读 1,332评论 0 3
  • 机器学习工程师纳米学位 模型评价与验证 项目 : 预测波士顿房价 第一步. 导入数据 在这个项目中,你将利用马萨诸...
    代号027阅读 3,802评论 0 1