TensorFlow 是咱们机器学习领域非常常用的一个组件, 它在数据处理, 模型建立, 模型验证等等关于机器学习方面的领域都有很好的表现, 前面的一节我已经简单介绍了一下 TensorFlow 里面基础的数据结构即: Tensor 和 Dataset; 这里咱们开始介绍 TensorFlow 的建模过程以及验证模型的一些简单方法. 其实无论是 sklearn 还是 TensorFlow, 他们的模型建立过程都是相似的, 都是经历 columns 类型声明, 模型定义, 数据训练, validation 等等几个步骤. 前面的几节内容我已经简单的介绍了如何用 sklearn 建立 tree_based 模型, 这里我主要是想演示 TensorFlow 的应用, 所以我就用 linear regressor 来当做例子来演示 TensorFlow 是如何从数据加载一直到数据验证的阶段. 至于线性拟合的实现的具体细节, 我在下一节的内容会从数学的角度具体解释的. 本节内容所使用的数据都是来自于网络中, 大家可以忽略具体的数据的意思, 主要的了解 TensorFlow 的应用过程, 不必过于纠结于模型的细节部分, 模型的细节我会在随后的章节解释. 好了, 那么咱们现在开始吧
第一步: 数据准备
顾名思义, 就是咱们准备数据的过程, 这里包括有 missing value handling, categorical data encoding,data split, data permutation 等等内容, 这一步咱们要将咱们将来模型训练所用到的数据都能准备好. 这个准备过程无非也就是上面的这些步骤, 咱们可以看下面的代码演示
- cali_housing_dataset_original = pd.read_csv("https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv")
- cali_housing_dataset_original["median_house_value"] /= 1000.0
- #create a random generator
- generator = np.random.Generator(np.random.PCG64())
- #permutate the data frame
- cali_housing_dataset_permutation = cali_housing_dataset_original.reindex(
- generator.permutation(cali_housing_dataset_original.index)
- )
- cali_housing_dataset_permutation.describe()
- #select the features that we will use in the model trainning process
- my_feature = cali_housing_dataset_permutation[["total_rooms"]]
- #select the targets of the dataset
- targets = cali_housing_dataset_permutation[["median_house_value"]]
这里我就不演示那些 feature engineering 的过程, 那些内容太多, 大家可以看我之前的博客, 这里主要是想向大家演示一下 permutation 的过程. 因为最新的 numpy 对于 randomize 的过程已经有更新了, 不再使用的那些老的 API 了, numpy 中最新的 randomize 的过程是通过创建 2 个 generator 来实现随机过程和随机数的生成了, 这两个 generator 一个个是 bit generator, 就如咱们上面代码中的 PCG64(), 它能产生一个随机的 bit stream, 根据 numpy 的官方文档, 虽然有很多种 bit generator, 但是 PCG64 是最稳定的一种; 另外一个就是 Generator, 它是通过 np.random.Generator()函数来实例化一个对象的, 它能将 bit generator 产生的 bit stream 转化成数字. 这里的数据咱们就选择一个最最简单的 linear regression 的方式, 那就是只选择一个 feature, 那就是 total_rooms; 咱们的 target 是 median_house_value.
第二步: 定义 feature 类型 和 optimizer
既然咱们的数据都准备好了, 那么之后那么得定义一下咱们数据的类型(每一个 column 的类型都得定义), 将来在咱们定义模型的时候咱们也得将 columns 的信息传递给咱们的模型对象; 以及用什么 optimizer 将来来训练咱们的模型, 这个 optimizer 将来也得传递给咱们的模型对象. 具体 optimizer 是什么我下面会慢慢讲的. 为了方便演示, 还是先上代码给大家看
- #indicates what is the data type of the feature to tensorflow
- feature_columns = [tf.feature_column.numeric_column("total_rooms")]
- #using stochastic gradient descent as the optimizer for our model
- #to ensure the magtitute of gradient do not become too large, we apply clipping_norm to our optimizer
- my_optimizer = tf.optimizers.SGD(learning_rate = 0.0000001, clipnorm=5.0)
从上面的代码可以看出, 第一句是声明咱们 feature_columns 里面只有一个 numeric_column, 记住每一个 column 都有一个 feature_column 对象, 这里因为咱们只选取了一个 feature 是 total_rooms, 所以咱们这里就一个 tf.feature_column 对象. 这里的第二行代码是咱们的重点, 也是以后优化模型中经常要调整的部分, 这里咱们可以看出, 这里的 optimizer 是一个 SGD, SGD 是 stochastic gradient descent 的缩写, 就是每一次计算咱们的 gradient descent 的时候, 咱们只选取一组数据进行计算, 如果每一次计算 gradient descent 的时候咱们都用整个数据进行计算, 那么咱们的计算机是负担不起的, 消耗的存储空间和计算能力都太大了, 因为在实际中咱们的数据集的数量经常都是以万为单位的. 具体计算 gradient descent 的过程我会在下一节中讲述模型训练过程中演示的. 咱们可以看出来, SGD 中还有两个参数, 分别是 learning_rate 和 clipnorm, 咱们知道, 当我们在训练我们的模型的时候, 我们需要逐步的训练很多次, 知道咱们的 gradient descent 趋于 0 是才停止, 咱们的每一步的大小都要合理, 如果 learning_rate 太小, 咱们训练的步数就会太多, 影响咱们的效率; 如果 learning_rate 太大, 则可能导致咱们训练模型的过程不能 converge, 就是永远找不到那个最小值, 从而导致训练的模型失败; 为了防止咱们咱们的 gradient 太大, 我们这里不单单用 learning_rate 来限制, 咱们还加了一个 clipnorm 来限制咱们的 gradient magtitute 大小, 防止咱们 fail to converge, 这相当于一个双重保险.
第三步: 定义一个模型 model
将上面的参数都定义完成后, 咱们就得定义一下咱们的模型啦, TensorFlow 提供了大量的模型可供使用, 几乎所有主流的机器学习的模型和深度学习相关的模型, TensorFlow 几乎实现全覆盖了, 具体咱们可以去他的官网查询, 他的官网地址是: https://www.tensorflow.org/api_docs/python/tf , 记住在 TensorFlow 中, 他的模型都在 tf.estimator 这个模块中. 因为这里是咱们讲述用 TensorFlow 开发机器学习应用的入门, 咱们就选一个最简单的模型 linear regressor 模型来演示
- linear_regressor = tf.estimator.LinearRegressor(
- feature_columns = feature_columns,
- optimizer = my_optimizer
- )
这里咱们可以看出是如何初始化一个 LinearRegressor 对象的, 同样的, 咱们可以看出来它初始化的时候也是需要传递 feature_columns 和 optimizer 这 2 个参数的, 而这两个参数正是咱们第二步中所初始化的, 可以说是环环相扣啊, 哈哈, 也可以看出咱们前面定义初始化的一个对象都是没有多余的, 都是要用到的. 这两个参数分别告诉了咱们的模型咱们数据 columns 的类型以及用什么 optimizer 来训练这 2 个信息.
第四步: 数据源 input_fn
既然咱们的原始数据准备好了, 模型也都定义好了, 如果需要训练咱们的模型, 咱们还差什么呢? 对了就是将咱们的原始数据 (这里的例子是 dataframe) 转化成 TensorFlow 中的 dataset, 并将转化后的 data 传递给咱们的模型, 让咱们之前定义的模型用这些数据去训练. 这里应该也是咱们用 TensorFlow 来建模的一个核心部分了, 咱们先看代码演示, 然后我会逐个详细解释的
- def my_input(features, targets, batch_size=500, shuffle=True, num_epochs=None):
- """
- epochs: while trainning, in the case of steps is larger than the length of dataset, we set the epochs to None, which means repeat forever. However,
- in trainning process, we have the steps to control the total number of iterations. While in the case of making predictions of a given dataset, we must
- set epochs to 1 and shuffle to False. Because we only want the input function return the dataset once, otherwise the function will keep returnning the
- results forvere once and once again.
- shuffle: in the trainning process, in order to balance the dataset, we set it to True. However while in prediction process, we have to set it to False, which
- could help us to evaluate the outputs. For example, if the outputs are shuffled, we have no way to compare the outputs to our original lables.
- """
- #convert panda dataframe to a dict of Numpy array
- features = {key:tf.multiply(np.array(value),1) for key,value in dict(features).items()}
- #construct a dataset
- ds = tf.data.Dataset.from_tensor_slices((features,targets))
- ds = ds.batch(batch_size).repeat(num_epochs)
- if shuffle:
- ds = ds.shuffle(buffer_size = 10000)
- return ds
这里有几个核心的参数我需要解释一下, 首先 features 和 target 两个参数很明显就是咱们第一步中获取的数据, 分别是用来训练这个模型的特征变量和 label; 重点这里解释一下 batch_size, shuffle 和 num_epochs 这三个参数, 这三个参数在咱们 TensorFlow 的整个学习过程中都得用到, 绝对的重点, 不容怀疑. 首先咱们来看 batch_size, 因为 SGD 每一次都只会选用一条数据来计算咱们的 gradient(训练模型), 而咱们的数据具有很强的随机性, 那么就会导致咱们的模型最后很可能不可用, 但是如果咱们每一个 step 都选用整个 dataset 来训练模型, 又会导致咱们的训练过程非常漫长, 那么聪明的人类就自然而然的想到了咱们可以每一次选用一定数量的数据来训练模型, 一般的数据量咱们大致的范围都是在 10-10000 之间, 这种方式就成为 mini-batch SGD, 在这里咱们就是采用了这种方式, 咱们每一次选用 500 条数据来训练咱们的模型, 这是通过设置 batch_size 的值来实现的. 对于 shuffle 这个参数呢, 也是为了打乱咱们的数据来进行训练, 最终的目的也是为了能帮助咱们训练出更加精确的模型, 防止咱们的数据分布不合理导致模型有偏差, 它里面的 buffer_size 的意思是先从 ds 中选中 buffer_size 个数据 (这些数据暂时还是有序的, 顺序和 ds 中一样), 然后 iterate 的时候呢, 就从这个 buffer 中随机的选择数据(这个选择数据的过程就是无序的选择了, 实现了随机的目的). 最后还有这个 repeat(num_epochs) 的函数, 首先 repeat 函数在 training 的过程中一定需要的, 因为当咱们设置 steps 步数参数的时候, 如果 steps 的总数要多余整个 dataset 的数据量的时候, 那么这时候咱们一定得重复利用咱们的 dataset 来达到训练的目的, 否则咱们的数据源的数据量不够了, 会出错的, 这就是为什么需要 repeat 的原因, 至于 num_epochs 是来控制重复的次数的, 一般在 training 阶段咱们将它设置成 None, 意思就是无限循环, 知道 training 中的 steps 全部走完位置, 如果在 predict 阶段, 咱们一般就设置成 1, 因为咱们验证阶段不需要重复的, 同样的在 predict 的时候, 数据源函数中的 shuffle 也要设置成 False 的, 否则 predict 的结果都是乱序的, 无法跟原来的数据进行对比了. 前面几个参数在咱们模型训练过程中可以说是最重要的参数了, 这里说的也比较多, 所以一点得用心搞明白.
第五步: 训练模型 training
既然上面咱们把模型的参数都设置好了, 数据源也定义好了, 那么接下来咱们的任务就是训练咱们定义的模型了, 这个过程在代码中是很简单的, 但它内部做的工作是很多的, 它需要计算在每个维度 (feature) 上的 gradient descent, 知道它趋于 0 为止, 它的计算细节我会在接下来的一个章节中展示出来, 咱们先看代码
- linear_regressor.train(
- input_fn = lambda:my_input(my_feature, targets),
- steps = 1000
- )
是不是很简单, 咱们只需要将数据源函数作为参数传递给他, 并且设置一个 steps 就可以了. 这里 input_fn 我就不解释了, 简单说一下 steps,steps 指的是咱们训练的步数, 咱们每计算一次 gradient descent, 就算一步, 这里指咱们最多计算 1000 次, 即使 1000 的时候 gradient descent 不等于 0 咱也停止咱的训练过程, 然后咱们可以重新设置 optimizer 中的 learning_rate 来重新训练.
第六步: predict 和 evaluate
经过前面五步后, 咱们已经训练出来了一个模型, 那么接下来咱们需要用这个模型来预测一下数据, 并且看看它的效果, 去 evaluate 一下这个模型. 正常的情况下咱们会将数据 split 成 training data 和 validation data, 这里我为了方便, 咱就直接用 training data 来演示如何 predict 还有如何 evaluate 咱们的模型. 简单的代码如下
- #create a input function for prediction
- prediction_input_fn = lambda:my_input(my_feature,targets,shuffle=False,num_epochs=1)
- #prediction
- predictions = linear_regressor.predict(input_fn = prediction_input_fn)
- predictions = np.array([item["predictions"][0] for item in predictions])
- #errors MSE
- mean_squared_error(targets,predictions)
在咱们做 prediction 的时候, 咱们也是需要数据源 input_fn 的, 在 prediction 的时候, shuffle=False, num_epochs=1; 然后调用这个模型 linear_regressor 的 predict 方法, 将数据源函数传递给他, 它返回的结果是一个 list, 这个 list 里面的 element 是一个 dictionary, 这个 dictionary 的 key 值 "predictions", value 也是一个一个 list, 并且只有一个元素 element, 此 element 就是咱们要的结果. 最后咱们要 evaluate 这个模型预测的结果, 咱们有很多种方式可以验证, 这里只展示了一个最简单的方式, 就是计算咱们的 target 和 prediction 的方差, 其实有很多很多种方式, 在后面的章节我会慢慢介绍.
第七步: data visualization
好了, 最后咱们来看一下根据咱们的学习的模型, 咱们想看看它的具体的拟合的效果, 这里就需要用一点之前咱们学习的数据可视化的内容了, 这里比较简单, 咱们通过模型学习到的参数, 画一条拟合线, 然后在将数据画到画布上, 坐标分别是 "total_rooms" 和 "house_median_price", 然后通过 scatter plot 展示出来. 代码如下
- sample = cali_housing_dataset_permutation.sample(n=300)
- x_0 = sample["total_rooms"].min()
- x_1 = sample["total_rooms"].max()
- linear_regressor.get_variable_names()#retrieve the name of the variable
- weights = linear_regressor.get_variable_value("linear/linear_model/total_rooms/weights")[0]#returns the value of variable given by name
- bias = linear_regressor.get_variable_value("linear/linear_model/bias_weights")#retruns the value of bias
- y_0 = weights*x_0+bias
- y_1 = weights*x_1+bias
- plt.plot([x_0,x_1],[y_0,y_1])#plot our regression line
- plt.ylabel("median_house_value")#label the y Axes
- plt.xlabel("total_rooms")#label the x Axes
- plt.scatter(sample["total_rooms"],sample["median_house_value"])#plot a scatter plot from the sample
- plt.show()
结果如下
可以看得出来, 拟合的还不错. 嘿嘿, 关于模型训练过程的可视化, 后面还有很多种, 以后我慢慢说, 例如: x 坐标是 steps, y 坐标是 loss, 也是非常常见的一种方式.
总结
今天完整的展示了用 TensorFlow 创建模型的整个过程, 一直从 data preparation 到最后的 evaluation, 可以说贯穿了 TensorFlow 开发机器学习应用的整个过程. 今天先用一个最简单的线性拟合例子展示这个过程, 后面我还会展示更多的更加复杂的模型, 例如: Logistic Regression, DNN, LSTM, 等等等等. 但是万变不离其宗, 他们的基础步骤都是上面的七个步骤. 最后 , 祝武汉加油!!!!!!!!
来源: https://www.cnblogs.com/tangxiaobo199181/p/12241170.html