人工智能小白日记之6 ML学习篇之2线性回归实战精读版

线性回归实战

接着上篇5-2 first_steps_with_tensor_flow 。 该篇拿到的是加利福尼亚州 1990 年的人口普查数据,将尝试预测 median_house_value,它将是我们的标签y(有时也称为目标)。我们将使用 total_rooms 作为输入特征x,这是一个典型的线性回归问题。

ps:第一次写忽略了很多东西,我重制了这篇,把代码逐行解释。因为初学阶段,如果跳得太快,很多内容后面读起来就很费力,所以这里做了代码精读。至于一些强大的内裤,我们需要时间积累,慢慢了解api和整个架构,这里做适当解读。

1 数据源及数据初始化

同样是远程文件的形式拿到训练数据

from __future__ import print_function

import math

from IPython import display
from matplotlib import cm
from matplotlib import gridspec
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
from sklearn import metrics
import tensorflow as tf
from tensorflow.python.data import Dataset

tf.logging.set_verbosity(tf.logging.ERROR)
pd.options.display.max_rows = 10
pd.options.display.float_format = '{:.1f}'.format

#如果出现证书问题,添加以下代码
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

#获取dataframe
california_housing_dataframe = 
pd.read_csv("https://download.mlcc.google.cn/mledu-datasets/california_housing_train.csv", sep=",")

#对数据进行随机化处理,以确保不会出现任何病态排序结果
california_housing_dataframe = california_housing_dataframe.reindex(
    np.random.permutation(california_housing_dataframe.index))
    
#将 median_house_value 调整为以千为单位,这样,模型就能够以常用范
#围内的学习速率较为轻松地学习这些数据。
california_housing_dataframe["median_house_value"] /= 1000.0

#一些实用统计信息快速摘要:样本数、均值、标准偏差、最大值、最小值和各种分位数
california_housing_dataframe.describe()

(ps:第一次看到这么多库,我们慢慢积累一下,不然后面可能就眼花缭乱了。)

知识扩展:
1)from future import print_function :这是python的future用法,Python提供了__future__模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性。

2)from IPython import display:IPython的介绍大家可以搜一下,是python的一个强大的交互式shell。https://blog.csdn.net/gavin_john/article/details/53086766

3)from matplotlib import XXX:从matplotlib中导入了三个函数:cm,gridspec,pyplot。Matplotlib 是一个 Python 的 2D绘图库,matplotlib.cm是matplotlib库中内置的色彩映射函数,matplotlib.gridspec可以定义子图, matplotlib.pyplot是一些命令行风格函数的集合,使matplotlib以类似于MATLAB的方式工作。

4)NumPy : 主要用于对多维数组执行计算
https://www.numpy.org.cn/article/basics/understanding_numpy.html

pandas 是基于NumPy 的一种工具, 纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

5)sklearn : scikit-learn 是基于 Python 语言的机器学习工具,库的算法主要有四类:分类,回归,聚类,降维。
http://sklearn.apachecn.org/#/

6)Dataset API是TensorFlow 1.3版本中引入的一个新的模块,主要服务于数据读取,构建输入数据的pipeline。

7)tf.logging tensorflow提供的日志工具

8)pd.options.display 对数据显示进行设置,max_rows最大行数为10,float_format浮点数保留一位小数

运行结果:
在这里插入图片描述

2 构建模型过程

拿到数据后,开始构建模型。

这是个线性回归问题,使用 TensorFlow Estimator API 提供的 LinearRegressor 接口。此 API 负责处理大量低级别模型搭建工作,并会提供执行模型训练、评估和推理的便利方法。

2-1 定义特征(x)并配置特征列

为了将我们的训练数据导入 TensorFlow,我们需要指定每个特征包含的数据类型。在本练习及今后的练习中,我们主要会使用以下两类数据:

  • 分类数据:一种文字数据。在本练习中,我们的住房数据集不包含任何分类特征,但您可能会看到的示例包括家居风格以及房地产广告词。
  • 数值数据:一种数字(整数或浮点数)数据以及您希望视为数字的数据。有时您可能会希望将数值数据(例如邮政编码)视为分类数据(我们将在稍后的部分对此进行详细说明)。

在 TensorFlow 中,我们使用一种称为“特征列”的结构来表示特征的数据类型。特征列仅存储对特征数据的描述;不包含特征数据本身。

这里的特征是total_rooms,从数据源中提取数据。并使用 numeric_column 定义特征列,这样会将其数据指定为数值。

# Define the input feature: total_rooms.
my_feature = california_housing_dataframe[["total_rooms"]]

# Configure a numeric feature column for total_rooms.
feature_columns = [tf.feature_column.numeric_column("total_rooms")]

知识扩展:
1)特征列:模型不可能无限度的接收任何类型的数据,我们可以转化、提炼、标准化我们的特征集合,从而可以进行TF所支持的类型的特征被模型所运算。
2)tf.feature_column: TF的特征列构造由此模块提供
参考:https://blog.csdn.net/u014021893/article/details/80423112
3)打印一下my_feature
在这里插入图片描述
这是一个pandas的Series格式的数据,这个在前面有一节学习pandas的时候有了解到

4)打印一下feature_columns
在这里插入图片描述
可以看出特征列就是对特征的描述,相当于元数据。名称叫total_rooms,形状是个长度1的元组,默认None,类型是tf.float32,normalizer_fn : 归一化函数,默认None。

2-2 定义目标(标签y)

# Define the label.
targets = california_housing_dataframe["median_house_value"]

也打印一下,
在这里插入图片描述

2-3 配置模型(y=wx+b)

接下来,我们将使用 LinearRegressor 配置线性回归模型,并使用 GradientDescentOptimizer(它会实现小批量随机梯度下降法 (SGD))训练该模型。learning_rate 参数可控制梯度步长的大小。

为了安全起见,我们还会通过 clip_gradients_by_norm 将梯度裁剪应用到我们的优化器。梯度裁剪可确保梯度大小在训练期间不会变得过大,梯度过大会导致梯度下降法失败。

# Use gradient descent as the optimizer for training the model.
my_optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.0000001)
my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)

# Configure the linear regression model with our feature columns and optimizer.
# Set a learning rate of 0.0000001 for Gradient Descent.
#使用 LinearRegressor 配置线性回归模型,理解成y=ax+b
linear_regressor = tf.estimator.LinearRegressor(
    feature_columns=feature_columns,
    optimizer=my_optimizer
)

知识扩展:
1) tf.estimator.LinearRegressor 是tensorflow高级api estimator中的接口,查看api当然时刻回到官网https://tensorflow.google.cn,然后这里:
在这里插入图片描述
在这里插入图片描述
当然也可以直接右上角搜索LinearRegressor
在这里插入图片描述
作为了解api,我们需要了解几点:
1.what is it ?
An estimator for TensorFlow Linear regression problems.
TF提供的一个关于线性回归问题的评估器

Train a linear regression model to predict label value given observation of feature values.
可以用来训练线性回归模型,通过已知特征x,预测目标(标签y)。

2.how to use it?
官方直接给了一个demo。这里不细讲了,我们知道有这东西,多接触几次就慢慢了解了,提升下效率。
这里有个哥们做了解读可以看看:https://blog.csdn.net/weixin_42499236/article/details/84257411

2-4 定义输入函数(迭代方式)

要将加利福尼亚州住房数据导入 LinearRegressor,我们需要定义一个输入函数,让它告诉 TensorFlow 如何对数据进行预处理,以及在模型训练期间如何批处理、随机处理和重复数据。也就是前面说过的某种迭代方法。

参数含义:
features: pandas DataFrame of features
targets: pandas DataFrame of targets
batch_size:将数据拆分成大小为 batch_size 的多批数据

shuffle:如果 shuffle 设置为 True,则我们会对数据进行随机处理,以便数据在训练期间以随机方式传递到模型。

num_epochs:按照指定周期数 (num_epochs) 进行重复。如果将默认值 num_epochs=None 传递到 repeat(),输入数据会无限期重复。

函数返回:Tuple of (features, labels) for next data batch

def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):

    # Convert pandas data into a dict of np arrays.
    #np指的是Numpy,是一个快速处理数据的内裤
    features = {key:np.array(value) for key,value in dict(features).items()}                                           
 
    # Construct a dataset, and configure batching/repeating.
    ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit
    ds = ds.batch(batch_size).repeat(num_epochs)
    
    # Shuffle the data, if specified.
    if shuffle:
      ds = ds.shuffle(buffer_size=10000)
    
    # Return the next batch of data.
    features, labels = ds.make_one_shot_iterator().get_next()
    return features, labels

知识扩展:
1) features = {key:np.array(value) for key,value in dict(features).items()}
这个处理过程在干嘛?
我们还记得前面打印的my_feature吗,是个pandas格式的数据,这里再打印一下,
在这里插入图片描述
np.array构建了一个数组,这句最后的结果是把那个pandas的DataFrame表格格式化成这么一个dict或map

2)Dataset.from_tensor_slices((features,targets)) :
Dataset刚才提到了是api的内容,直接查询api,注意这里的两个括号,里面的(features,targets)是传入参数
在这里插入图片描述
从给出的tensors中分片出来得到一个Dataset,要求每个tensor第0维度的尺寸要一致。其他内容是一些注意事项,包含的NumPy数组不要太大,否则要考虑替代方案

emm…这是不是太简洁了额… ,对于初学者而言,这显然看不太懂啊。tensor,也就是张量,看看接收的参数(features,targets),我把它打印出来:
在这里插入图片描述
好吧,也算是一个tensor,左边的features是个dict结构,右边的targets是个pandas的Series结构。然后通过Dataset.from_tensor_slices((features,targets))构建了一个Dataset,打印一下
在这里插入图片描述
打印出来的是一个Dataset的描述。包含了这个Dataset的名称,shapes形状,以及types描述。发现没,里面的两个小括号(),这里把targets直接当作数组在用。

3)接着,对这个Dataset进行分批处理和重复,随机化

ds = ds.batch(10).repeat(None)
print(ds)
ds = ds.shuffle(buffer_size=10000)
print(ds)  

打印出来是这样
在这里插入图片描述
4)最后通过dataset的迭代器开始迭代数据,返回当次的分批数据

features1, labels1 = ds.make_one_shot_iterator().get_next()
print(features1)
print(labels1)

在这里插入图片描述
所以可以看出来,这个数据处理的时候,还是分为特征和标签单独处理的,采用(features,labels)的绑定方式可以统一处理,特别是随机化的过程,为了保证feature和label对应,必须绑定处理

2-5 训练模型

_ = linear_regressor.train(
    input_fn = lambda:my_input_fn(my_feature, targets),
    steps=100
)

ps:python的lambda表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。这里会按照迭代方法训练 100 步。

2-6 评估模型

# Create an input function for predictions.
# Note: Since we're making just one prediction for each example, we don't 
# need to repeat or shuffle the data here.
prediction_input_fn =lambda: my_input_fn(my_feature, targets, num_epochs=1, shuffle=False)

# Call predict() on the linear_regressor to make predictions.
predictions = linear_regressor.predict(input_fn=prediction_input_fn)

# Format predictions as a NumPy array, so we can calculate error metrics.
predictions = np.array([item['predictions'][0] for item in predictions])

# Print Mean Squared Error and Root Mean Squared Error.
mean_squared_error = metrics.mean_squared_error(predictions, targets)
root_mean_squared_error = math.sqrt(mean_squared_error)
print("Mean Squared Error (on training data): %0.3f" % mean_squared_error)
print("Root Mean Squared Error (on training data): %0.3f" % root_mean_squared_error)

ps:这里打印了MSE均方误差和RMSE均方根误差,R代表在MSE的基础上开平方。
执行结果:
在这里插入图片描述
这个结果貌似无法评判误差大小,模型的好坏。可以弄点参照:

min_house_value = california_housing_dataframe["median_house_value"].min()
max_house_value = california_housing_dataframe["median_house_value"].max()
min_max_difference = max_house_value - min_house_value

print("Min. Median House Value: %0.3f" % min_house_value)
print("Max. Median House Value: %0.3f" % max_house_value)
print("Difference between Min. and Max.: %0.3f" % min_max_difference)
print("Root Mean Squared Error: %0.3f" % root_mean_squared_error)

执行结果:
在这里插入图片描述
误差跨越目标值的近一半范围。

2-7 降低误差

这是每个模型开发者都会烦恼的问题。我们来制定一些基本策略,以降低模型误差。

2-7-1 绘制散点图

首先,我们可以了解一下根据总体摘要统计信息,预测和目标的符合情况。

calibration_data = pd.DataFrame()
calibration_data["predictions"] = pd.Series(predictions)
calibration_data["targets"] = pd.Series(targets)
calibration_data.describe()

执行结果:
在这里插入图片描述
可以看出偏差是有点大的。为了更加直观,可以用线性回归一节的散点图和预测画线,将其具现化。

#获取均匀分布的随机数据样本,以便绘制可辨的散点图。
sample = california_housing_dataframe.sample(n=300)

# Get the min and max total_rooms values.
x_0 = sample["total_rooms"].min()
x_1 = sample["total_rooms"].max()

# Retrieve the final weight and bias generated during training.
weight = linear_regressor.get_variable_value('linear/linear_model/total_rooms/weights')[0]
bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')

# Get the predicted median_house_values for the min and max total_rooms values.
y_0 = weight * x_0 + bias 
y_1 = weight * x_1 + bias

# Plot our regression line from (x_0, y_0) to (x_1, y_1).
plt.plot([x_0, x_1], [y_0, y_1], c='r')

# Label the graph axes.
plt.ylabel("median_house_value")
plt.xlabel("total_rooms")

# Plot a scatter plot from our data sample.
plt.scatter(sample["total_rooms"], sample["median_house_value"])

# Display graph.
plt.show()

ps:plt绘图属于matplotlib内裤中的功能,可以去搜索一下,plot画线,scatter画散点图。
执行结果:
在这里插入图片描述
很明显这条初始线是不行的,跟摘要统计信息的结果一致。

知识扩展:

  1. DataFrame.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)
    n 整型,表示取样数量
    frac 浮点型,表示取样比例,与n两者选一不能共用,frac=None时n默认为1
    replace 布尔值,样品是否更换,没发现有啥用?
    weights 给每个抽样赋予权重
    random_state 随机数生成器,可以让抽样基于指定随机数
    axis的含义是抽样的方向,axis=0,对行进行抽样,axis=1,对列进行抽样

2)linear_regressor.get_variable_value 获取weight和bias,貌似查不到什么解释,后面那一串参数代表什么,以后我们慢慢发现?

剩下的就比较明显了,不说了

2-7-2 调整模型超参数

将以上步骤整合,并留出超参数的位置,learning_rate学习速率,steps执行总步数,batch_size批量拆分大小。这样在调节这几个参数的时候可以直接看到效果。

def train_model(learning_rate, steps, batch_size, input_feature="total_rooms"):
  """Trains a linear regression model of one feature.
  
  Args:
    learning_rate: A `float`, the learning rate.
    steps: A non-zero `int`, the total number of training steps. A training step
      consists of a forward and backward pass using a single batch.
    batch_size: A non-zero `int`, the batch size.
    input_feature: A `string` specifying a column from `california_housing_dataframe`
      to use as input feature.
  """
  
  periods = 10
  steps_per_period = steps / periods

  my_feature = input_feature
  my_feature_data = california_housing_dataframe[[my_feature]]
  my_label = "median_house_value"
  targets = california_housing_dataframe[my_label]

  # Create feature columns.
  feature_columns = [tf.feature_column.numeric_column(my_feature)]
  
  # Create input functions.
  training_input_fn = lambda:my_input_fn(my_feature_data, targets, batch_size=batch_size)
  prediction_input_fn = lambda: my_input_fn(my_feature_data, targets, num_epochs=1, shuffle=False)
  
  # Create a linear regressor object.
  my_optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
  my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
  linear_regressor = tf.estimator.LinearRegressor(
      feature_columns=feature_columns,
      optimizer=my_optimizer
  )

  # Set up to plot the state of our model's line each period.
  plt.figure(figsize=(15, 6))
  plt.subplot(1, 2, 1)
  plt.title("Learned Line by Period")
  plt.ylabel(my_label)
  plt.xlabel(my_feature)
  sample = california_housing_dataframe.sample(n=300)
  plt.scatter(sample[my_feature], sample[my_label])
  colors = [cm.coolwarm(x) for x in np.linspace(-1, 1, periods)]

  # Train the model, but do so inside a loop so that we can periodically assess
  # loss metrics.
  print("Training model...")
  print("RMSE (on training data):")
  root_mean_squared_errors = []
  for period in range (0, periods):
    # Train the model, starting from the prior state.
    linear_regressor.train(
        input_fn=training_input_fn,
        steps=steps_per_period
    )
    # Take a break and compute predictions.
    predictions = linear_regressor.predict(input_fn=prediction_input_fn)
    predictions = np.array([item['predictions'][0] for item in predictions])
    
    # Compute loss.
    root_mean_squared_error = math.sqrt(
        metrics.mean_squared_error(predictions, targets))
    # Occasionally print the current loss.
    print("  period %02d : %0.2f" % (period, root_mean_squared_error))
    # Add the loss metrics from this period to our list.
    root_mean_squared_errors.append(root_mean_squared_error)
    # Finally, track the weights and biases over time.
    # Apply some math to ensure that the data and line are plotted neatly.
    y_extents = np.array([0, sample[my_label].max()])
    
    weight = linear_regressor.get_variable_value('linear/linear_model/%s/weights' % input_feature)[0]
    bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')

    x_extents = (y_extents - bias) / weight
    x_extents = np.maximum(np.minimum(x_extents,
                                      sample[my_feature].max()),
                           sample[my_feature].min())
    y_extents = weight * x_extents + bias
    plt.plot(x_extents, y_extents, color=colors[period]) 
  print("Model training finished.")

  # Output a graph of loss metrics over periods.
  plt.subplot(1, 2, 2)
  plt.ylabel('RMSE')
  plt.xlabel('Periods')
  plt.title("Root Mean Squared Error vs. Periods")
  plt.tight_layout()
  plt.plot(root_mean_squared_errors)

  # Output a table with calibration data.
  calibration_data = pd.DataFrame()
  calibration_data["predictions"] = pd.Series(predictions)
  calibration_data["targets"] = pd.Series(targets)
  display.display(calibration_data.describe())

  print("Final RMSE (on training data): %0.2f" % root_mean_squared_error)

核心代码在这里:
在这里插入图片描述
将步数steps划分为periods=10个阶段,每个阶段相继训练,预测并绘图。

然后调用:

train_model(
    learning_rate=0.00001,
    steps=100,
    batch_size=1
)

在这里插入图片描述
通过调节这几个超参数,直到找到最佳拟合。

比如说,我调节的这个:

train_model(
    learning_rate=0.00004,
    steps=300,
    batch_size=10
)

在这里插入图片描述
知识扩展:
1) # Take a break and compute predictions.
predictions = linear_regressor.predict(input_fn=prediction_input_fn)
predictions = np.array([item[‘predictions’][0] for item in predictions])
预测这里的代码,我看的有点懵,预测之后,又多了一行处理,容我打印一下
在这里插入图片描述
第一行应该是得到一个预测的结果类,第二行从里面拿到预测结果转换为np数组。

3 小结

此篇,描述了线性回归实战内容,选取了一个特征,利用tensorflow的高级api estimator构建线性模型,训练及预测,然后是评估和散点图,最后通过提炼超参数来调节模型。

重点是掌握超参数对于模型调节的意义,然后积累一些库的用法。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值