头条项目推荐的相关技术(九):深度学习推荐系统与TensorFlow框架

1. 写在前面

这里是有关于一个头条推荐项目的学习笔记,主要是整理工业上的推荐系统用到的一些常用技术, 这是第九篇, 上一篇文章介绍了实时推荐的相关业务流以及ABTest的相关知识, 实时推荐里面必要重要的就是ABTest以及推荐中心的逻辑,至于真正的模型预测那块,实际上相对来说会比较简单, 到这里,实际上整个黑马头条推荐的业务流已经走完了, 从一开始的数据收集,数据处理到特征工程,离线各种服务的搭建和模型训练, 然后在线实时计算解决冷启动问题,再到上一篇的实时预测逻辑等,这其实就是整个推荐系统的基本流程了,当然这每个模块都有很多的细节和重点知识,这个需要结合着前面的文章以及代码来重点理解,代码仅仅看一遍是不够的,感觉都手撸至少一遍。 接下来还有两篇文章, 说的是排序模型这块的内容, 因为上面虽然走完了整个推荐流程,但是我们用的排序模型还是最简单的基线模型逻辑回归, 在深度学习时代,这个模型其实不是那么太常用了或者说已经被淘汰了(但不是说不重要哈), 基本上都是一些主流的深度学习模型,比如W&D, DIN这种了。 那么深度学习模型应该如何用到上面的搭建推荐系统中呢? 就是下面两篇的内容逻辑。

而这篇文章要介绍的就是推荐系统的深度学习相关知识以及TensorFlow的基础,因为深度学习模型如果搭建,工业上还是首选TF框架, 所以需要在真正搭建深度学习模型之前也学习一些基础的深度学习和框架相关知识, 下一篇文章就是深度学习模型在头条推荐系统中的搭建部署与服务。主要内容如下:

  • 深度学习到推荐系统
  • 深度学习应用简介以及TensorFlow框架初识
  • TensorFlow基础(tf2.0常用API介绍,tf.estimator介绍和使用)

Ok, let’s go!

2. 深度学习到推荐系统

2.1 深度学习发展成功与局限

最近几年深度学习的流行,大家一般认为是从2012年 AlexNet 在图像识别领域的成功作为一个里程碑。AlexNet 提升了整个业界对机器学习的接受程度:以前很多机器学习算法都处在“差不多能做 demo ”的程度,但是 AlexNet 的效果跨过了很多应用的门槛,造成了应用领域井喷式的兴趣。

  • 成功的原因:是大数据,是高性能计算。
    • 大量的数据,比如说移动互联网的兴起,以及 AWS 这样低成本获得标注数据的平台,使机器学习算法得以打破数据的限制;
    • 由于 GPU 等高性能运算的兴起,又使得我们可以在可以控制的时间内(以天为单位甚至更短)进行 exaflop 级别的计算,从而使得训练复杂网络变得可能。
  • 局限的地方:一点是结构化的理解,一点是小数据上的有效学习算法。
    • 很多深度学习的算法还是在感知这个层面上形成了突破,可以从语音、图像,这些非结构化的数据中进行识别的工作。面对更加结构化的问题的时候,简单地套用深度学习算法可能并不能达到很好的效果
    • 数据量非常小的时候,深度学习的复杂网络往往无法取得很好的效果,但是很多领域,特别是类似医疗这样的领域,数据是非常难获得的。

2.2 推荐系统的应用需求

实际上深度学习在自然语言处理,图像处理,图像识别等领域迅猛发展的近4年来,深度学习在其他领域,例如推荐系统也得到快速的发展。除了语音和图像之外,如何解决更多问题。在阿里、美团等很多互联网企业中有一个“沉默的大多数”的应用,就是推荐系统:它常常占据了超过80%甚至90%的机器学习算力,如何将深度学习和传统推荐系统进一步整合,如何寻找新的模型,如何对搜索和推荐的效果建模,也是公司不可缺少的技能

2.3 推荐系统为什么加入深度学习?

为什么我们会想到使用深度学习去处理推荐系统里面的事情呢,推荐系统从基于内容的推荐,到协同过滤的推荐,协同过滤的推荐在整个推荐算法领域里独领了多年。或许深度学习在推荐系统里面没有像图像处理算法那样一枝独秀,但是深度学习对于推荐系统的帮助确实起到了,推波助澜的功效。

  • 能够直接从内容中提取特征,表征能力强
  • 容易对噪声数据进行处理,抗噪能量强
  • 深度学习便于对负责数据进行统一处理

具体的基于深度的推荐模型后面讲TensorFlow详细介绍。

  • 图像: 识别,检测,分割, CNN,RCNN, Faster RCNN, YOLO, SSD
  • 自然语言处理: RNN,Transformer, Attention, BERT
  • 推荐系统: LR, FM, Wide&deep, DeepFM…

3. 深度学习应用简介

3.1 机器学习和深度学习的区别

首先先看下机器学习和深度学习的区别:

在这里插入图片描述
特征提取方面

  • 机器学习的特征工程步骤是要靠手动完成的,而且需要大量领域专业知识
  • 深度学习通常由多个层组成,它们通常将更简单的模型组合在一起,通过将数据从一层传递到另一层来构建更复杂的模型。通过大量数据的训练自动得到模型,不需要人工设计特征提取环节

深度学习算法试图从数据中学习高级功能,这是深度学习的一个非常独特的部分。因此,减少了为每个问题开发新特征提取器的任务。适合用在难提取特征的图像、语音、自然语言领域

数据量

  • 机器学习需要的执行时间远少于深度学习,深度学习参数往往很庞大,需要通过大量数据的多次优化来训练参数。

在这里插入图片描述

  • 第一、它们需要大量的训练数据集

  • 第二、是训练深度神经网络需要大量的算力

    可能要花费数天、甚至数周的时间,才能使用数百万张图像的数据集训练出一个深度网络。所以以后
    需要强大对的GPU服务器来进行计算
    全面管理的分布式训练与预测服务——比如谷歌 TensorFlow 云机器学习平台——可能会解决这些问题,为大家提供成本合理的基于云的 CPU 和 GPU

算法代表:

  • 机器学习: 朴素贝叶斯、决策树等
  • 深度学习: 神经网络等

3.2 深度学习的主要应用

  • 图像识别:物体识别、场景识别、车型识别、人脸检测跟踪、人脸关键点定位、人脸身份认证
  • 自然语言处理技术:机器翻译、文本识别、聊天对话、语音技术、语音识别
  • 推荐系统

前几年一直关注AI框架,但是近年来框架的同质化说明了它不再是一个需要花大精力解决的问题,TensorFlow、Pytorch这样的框架在工业界的广泛应用,以及各种框架利用 Python 在建模领域的优秀表现,已经可以帮助我们解决很多以前需要自己编程实现的问题,如果作为 AI 工程师,我们应该跳出框架的桎梏,往更广泛的领域寻找价值。

3.3 TensorFlow初识

官网:https://www.tensorflow.org/

  • 语言多样(Language Options)
    • TensorFlow使用C++实现的,然后用Python封装。谷歌号召社区通过SWIG开发更多的语言接口来支持TensorFlow
  • 使用分发策略进行分发训练
    • 对于大型 ML 训练任务,分发策略 API使在不更改模型定义的情况下,可以轻松地在不同的硬件配置上分发和训练模型。由于 TensorFlow 支持一系列硬件加速器,如 CPU、GPU 和 TPU
  • Tensorboard可视化
    • TensorBoard是TensorFlow的一组Web应用,用来监控TensorFlow运行过程
  • 在任何平台上的生产中进行强大的模型部署
    一旦您训练并保存了模型,就可以直接在应用程序中执行它,或者使用部署库为其提供服务:
    • TensorFlow 服务:允许模型通过 HTTP/REST 或 GRPC/协议缓冲区提供服务的 TensorFlow 库构建。
    • TensorFlow Lite:TensorFlow 针对移动和嵌入式设备的轻量级解决方案提供了在 Android、iOS 和嵌入式系统上部署模型的能力。
    • tensorflow.js:支持在 JavaScript 环境中部署模型,例如在 Web 浏览器或服务器端通过 Node.js 部署模型。TensorFlow.js 还支持在 JavaScript 中定义模型,并使用类似于 Kera 的 API 直接在 Web 浏览器中进行训练。

安装:安装 TensorFlow在64 位系统上测试这些系统支持 TensorFlow:

  • Ubuntu 16.04 或更高版本
  • Windows 7 或更高版本
  • macOS 10.12.6 (Sierra) 或更高版本(不支持 GPU)

进入虚拟环境当中再安装。刚开始的环境比较简单,只要下载tensorflow即可

环境包:安装较慢,指定镜像源,请在带有numpy等库的虚拟环境中安装

ubuntu安装

pip install tensorflow==1.13 -i https://mirrors.aliyun.com/pypi/simple

MacOS安装

pip install tensorflow==1.13 -i https://mirrors.aliyun.com/pypi/simple

注:如果需要下载GPU版本的(TensorFlow只提供windows和linux版本的,没有Macos版本的)参考官网

  1. 虚拟机下linux也是用不了GPU版本TensorFlow
  2. 本机单独的windows和本机单独的unbuntu可以使用GPU版本TensorFlow,需要安装相关驱动

Tenssorlfow使用技巧: 使用 tf.keras 构建、训练和验证您的模型,tf相关API用于损失计算修改等,tensorflow提供模型训练模型部署。

4. TensorFlow基础

4.1 TensorFlow1.x的一些知识要了解

这里简单介绍一些TensorFlow1.X的知识,可以帮助我们了解一些原来的背景知识。 TensorFlow的结构:

TensorFlow 程序通常被组织成一个构建图阶段和一个执行图阶段

在构建阶段,数据与操作的执行步骤被描述成一个图。

在执行阶段,使用会话执行构建好的图中的操作。

  • 图和会话 :
    • 图:这是 TensorFlow 将计算表示为指令之间的依赖关系的一种表示法
    • 会话:TensorFlow 跨一个或多个本地或远程设备运行数据流图的机制
  • 张量:TensorFlow 中的基本数据对象
  • 节点:提供图当中执行的操作

数据流图:

在这里插入图片描述
TensorFlow是一个采用数据流图(data flow graphs),用于数值计算的开源框架。

节点(Operation)在图中表示数学操作,线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

4.1.1 会话

一个运行TensorFlow operation的类。会话包含以下两种开启方式:

  • tf.Session:用于完整的程序当中
  • tf.InteractiveSession:用于交互式上下文中的TensorFlow ,例如shell

1 TensorFlow 使用 tf.Session 类来表示客户端程序(通常为 Python 程序,但也提供了使用其他语言的类似接口)与 C++ 运行时之间的连接

2 tf.Session 对象使用分布式 TensorFlow 运行时提供对本地计算机中的设备和远程设备的访问权限。

	# 开启会话
    with tf.Session() as sess:
        # sum_t = sess.run(c_t)
        # 想同时执行多个tensor
        print(sess.run([a_t, b_t, c_t]))
        # 方便获取张量值的方法
        # print("在sess当中的sum_t:\n", c_t.eval())
        # 会话的图属性
        print("会话的图属性:\n", sess.graph)

会话的run():

run(fetches,feed_dict=None, options=None, run_metadata=None)

通过使用sess.run()来运行operation

  • fetches:单一的operation,或者列表、元组(其它不属于tensorflow的类型不行)
  • feed_dict:参数允许调用者覆盖图中张量的值,运行时赋值
    与tf.placeholder搭配使用,则会检查值的形状是否与占位符兼容。

tf2.0已经把会话干掉。

4.1.2 feed操作

placeholder提供占位符,run时候通过feed_dict指定参数

	# 定义占位符
    a = tf.placeholder(tf.float32)
    b = tf.placeholder(tf.float32)
    sum_ab = tf.add(a, b)
    print("sum_ab:\n", sum_ab)
    # 开启会话
    with tf.Session() as sess:
        print("占位符的结果:\n", sess.run(sum_ab, feed_dict={
    
    a: 3.0, b: 4.0}))

请注意运行时候报的错误error:

  • RuntimeError:如果这Session是无效状态(例如已关闭)。
  • TypeError:如果fetches或者feed_dict键的类型不合适。
  • ValueError:如果fetches或feed_dict键无效或引用 Tensor不存在的键。

4.1.3 张量

TensorFlow 的张量就是一个 n 维数组, 类型为tf.Tensor。Tensor具有以下两个重要的属性

  • type:数据类型(tf.float32,64, tf.int64,32,16,8, tf.uint8, tf.string, tf.bool等)
  • shape:形状(阶)(tensor.shape)

创建张量的指令(tf2.0也会用):

  • 固定值张量
    在这里插入图片描述
  • 创建随机张量
    在这里插入图片描述

4.1.4 张量的变换

  1. 类型改变:
    在这里插入图片描述

  2. 形状改变
    TensorFlow的张量具有两种形状变换,动态形状和静态形状

    • tf.reshape
    • tf.set_shape

    关于动态形状和静态形状必须符合以下规则

    • 静态形状
      转换静态形状的时候,1-D到1-D,2-D到2-D,不能跨阶数改变形状
      对于已经固定的张量的静态形状的张量,不能再次设置静态形状
    • 动态形状
      tf.reshape()动态创建新张量时,张量的元素个数必须匹配
  3. 数学运算: 算法运算,基本数学函数,矩阵运算,reduce操作,序列索引操作,可以看官方文档

4.1.5 变量

TensorFlow变量是表示程序处理的共享持久状态的最佳方法。变量通过 tf.Variable OP类进行操作。变量的特点:

  • 存储持久化
  • 可修改值
  • 可指定被训练

创建变量

tf.Variable(initial_value=None,trainable=True,collections=None,name=None)

  • initial_value:初始化的值
  • trainable:是否被训练
  • collections:新变量将添加到列出的图的集合中collections,默认为[GraphKeys.GLOBAL_VARIABLES],如果trainable是True变量也被添加到图形集合 GraphKeys.TRAINABLE_VARIABLES
  • 变量需要显式初始化,才能运行值

关于1.x就先整理这么多,因为现在都慢慢转成2.X版本了, 可是大变样了啊,并且好用多了,1.x虽然还有在用的,但是我之前也没用过几次,即使训练网络也是用的keras的东西,所以对1.x了解的并不深, 这里我也不过多的去研究了,到时候真用的上了再去研究,下面主要学习下2.0怎么用。

4.2 TensorFlow2.0要会使用

TensorFlow 2.0 将专注于 简单性 和 易用性,具有以下更新:

  • 使用 Keras 和 eager execution,轻松构建模型
  • 在任意平台上实现生产环境的稳健模型部署
  • 为研究提供强大的实验工具
  • 通过清理废弃的 API 和减少重复来简化 API

TensorFlow API的使用图:重点知识
在这里插入图片描述

  1. 使用tf.data加载数据。使用输入管道读取训练数据,输入管道使用tf.data创建。利用tf.feature_column描述特征,如分段和特征交叉。

  2. 使用tf.keras构建、训练并验证模型,或者使用Premade Estimators

    • Keras与TensorFlow的其余部分紧密集成,因此用户可以随时访问TensorFlow的函数。如线性或逻辑回归、梯度上升树、随机森林等也可以直接使用(使用tf.estimatorAPI实现)。
    • 如果不想从头开始训练模型,用户也可以很快利用迁移学习来训练使用TensorFlow Hub模块的Keras或Estimator模型。(迁移学习)
  3. 使用分布式策略进行分布式训练。对于大型机器学习训练任务,分布式策略API可以轻松地在不同硬件配置上分配和训练模型,无需更改模型的定义。由于TensorFlow支持各种硬件加速器,如CPU,GPU和TPU,因此用户可以将训练负载分配到单节点/多加速器以及多节点/多加速器配置上(包括TPU Pod)。

  4. 导出到Saved Model。 TensorFlow将对Saved Model进行标准化,作为TensorFlow服务的一部分,他将成为TensorFlow Lite、TensorFlow.js、TensorFlow Hub等格式的可互换格式。

工作流程:

在这里插入图片描述
API 介绍:

  1. 高层API (High level): 包括Estimators、Keras以及预构建好的Premade estimator(如线性回归、逻辑回归这些、推荐排序模型wide&deep);
  2. 中层API (Mid level): 包括layers, datasets, loss和metrics等具有功能性的函数,例如网络层的定义,Loss Function,对结果的测量函数等;
  3. 底层API (Low level): 包括具体的加减乘除、具有解析式的数学函数、卷积、对Tensor属性的测量等。

在这里插入图片描述

4.3 tf.estimator使用入门

4.3.1 tf.extimator介绍

Tensorflow中的tf.estimator API封装了基础的机器学习模型。 Estimator是可扩展性最强且面向生产的TensorFlow模型类型。

Estimator是一种可几大简化机器学习编程的高阶TensorFlow API, Estimator会封装下列操作:

  • 训练
  • 评估
  • 预测
  • 导出以供使用

Estimator的优势:

  1. 可以在本地主机或分布式多服务器环境中运行基于Estimator的模型,无需更改模型。此外可以在CPU,TPU, GPU上运行模型,无序重新编码模型
  2. Estimator简化模型开发者之间共享实现的过程
  3. 可以使用高级直观代码开发先进模型
  4. Estimator本身在tf.layers上构建而成,可以简化自定义过程
  5. Estimator会构建好图
  6. Estimator提供安全的分布式训练循环, 可以控制如何以及何时:构件图,初始化变量,开始排队及处理异常,创建检查点文件并从故障恢复

依赖预创建的Estimator的TensorFlow程序通常包含下列四个步骤: 重点知识,注意这里是用tf提供的Estimator步骤,而不是Keras

  1. 编写一个或多个数据集导入函数。 例如,可以创建一个函数来导入训练集,并创建另一个函数来导入测试集。每个数据集导入函数必须返回两个对象:

    • 一个字典, 其中键是特征名称,值是包含相应特征数据的张量(或Sparse Tensor) — 特征值
    • 一个包含一个或多个标签的张量 — 目标值


    例如, 下面代码展示了输入函数的基本框架:

    def input_fn(dataset):
    	# 构建数据集
    	return feature_dict, label
    
  2. 定义特征列。每个tf.feature_column都标识了特征名称,特征类型和任何输入预处理操作。例如,以下代码段创建了三个存储整数或浮点数据的特征列。前两个特征列仅表示了特征的名称和类型。第三个特征列还指定了一个Lambda,该程序将调用Lambda来调节原始数据。 — 这里可以理解成特征工程部分

    population = tf.feature_column.numeric_column('population')
    crime_rate = tf.feature_column.numeric_column('crime_rate')
    median_education = tf.feature_column.numeric_column('median_education', normalizer_fn=lambda x: x - global_education_mean)
    
  3. 实例化相关的预创建的Estimator。例如,下面是对名为LinearClassifier的预创建Estimator进行实例化的示例代码:

    # 每一列都必须指定类型和处理方式
    lestimator = tf.estimator.LinearClassifier(feature_columns=[population, crime_rate, median_education])
    
  4. 调用训练,评估或推理方法。 例如,所有的Estimator都提供训练模型的train方法。

    # 训练这里就是提供数据,注意,这里提供的是我们输入函数的名称,不是具体函数,有这个名称就可以
    lestimator.train(input_fn=my_training_set, steps=200)
    

4.3.2 Premade Estimators

pre-made Estimator是基类tf.estimator.Estimator的子类,而自定义的模型是tf.estimator.Estimator的实例:

在这里插入图片描述
pre-made Estimators是已经做好的。 但有时候,需要对Estimator的行为做更多控制,需要自定义Estimator,这时候可以自定义。

4.3.3 案例: 使用美国普查数据分类

1994年和1995年的美国普查收入数据集。 解决的是二元分类问题,目标标签为如果输入超过5美元,该值为1,否则该值为0.
在这里插入图片描述
这些特征分为两类: 类别列和连续列

  • 如果某列的值只能是一个有限集合的类别之一,则为类别。比如上面的受教育程度,workclass
  • 如果连续范围内任意数组,连续列,如资本收益等

我这里整理这个表的原因是想学习下上面的API到底如何真实情况下使用。并没有这个数据集。

下面就是基于这个数据集, 把上面使用Estimator的流程走一下:

  1. 读取美国普查输入数据
    tf.data API 可以很方便以不同数据格式处理大量数据,以及处理复杂的转换

    • 读取csv文件接口: tf.data.TextLineDataset() — csv文件,文本文件都可以用这哥们
      • 路径 + 文件名称列表
      • 返回: Dataset结构

    读取的相关设置:

    # 列名指定
    _CSV_COLUMNS = ['age', 'workclass', 'fnlwgt', 'education', 'hours_per_week']
    _CSV_COLUMNS_DEFAULTS = [[0], [''], [0], [''], [0], [0]]
    
    train_file = '/adult.data'
    test_file = '/adult.test'
    

    输入函数代码:

    def input_fn(data_file, num_epochs, shuffle, batch_size):
    	# 这个函数其实还可以定义数据处理的逻辑,最后只要返回字典以及一个标签列即可
    	def parse_csv(value):
    		columns = tf.decode_csv(value, recored_default=_CSV_COLUMN_DEFAULTS)  # 把文本解析成列,如果里面有缺失的,根据这个默认值给填充
    		features = dict(zip(_CSV_COLUMNS, columns))
    		labels = features.pop('income_backet')
    		classes = tf.equal(labels, '>50k')
    		return features, classes      # 返回一个字典表示特征及值,一个列表标签
    	
    	# 读取CSV文件
    	dataset = tf.data.TextLineDataset(data_file)  # 理解成迭代器 一行一行的取数据,默认返回一行
    	dataset = dataset.map(parse_csv)
    	dataset = dataset.repeat(num_epochs)  # 重复个num_epochs遍
    	dataset = dataset.batch(batch_size)   # 再分成batch
    	return data
    
  2. 模型选择并进行特征工程处理
    Estimator使用名为特征列的极值来描述模型应该如何解读每个原始输入特征。 Estimator需要数值输入向量,而特征列会描述应如何转换每个特征。

    选择和创建一组正确的特征列是学习有效模型的关键。特征列可以是原始特征dict中的其中一个原始输入(基准特征列),也可以是对一个或多个基准列进行转换而创建的任意新列(衍生特征列)。

    特征列是一个抽象概念,表示可用于预测目标标签的任何原始向量或衍生变量

    1. 数值列
      最简单的feature_column是numer_column. 它表示特征是数值,应直接输入到模型中。

      age = tf.feature_column.numeric_column('age')
      ecucation_num = tf.feature_column.numeric_column('education_num')
      capital_gain = tf.feature_column.numeric_column('catital_gain')
      capital_loss = tf.feature_column.numeric_column('catital_loss')
      hours_per_week = tf.feature_column.numeric_column('hours_per_week')
      
      numeric_columns = [age, education_num, catital_gain, catital_loss, hours_per_week]
      
    2. 类别类
      要为类别特征定义特征列,使用tf.feature_column.categorical_column函数创建CategoricalColumn. 如果知道某个列的所有可能特征值的集合,并且集合中只有几个值,使用categorical_column_with_vocabulary_list。列表中的每个键会被自动分配递增的ID(0开始). 例如,对于relationshaip列,我们可以将整数ID 0分配给特征字符串Husband, 将1分配给Not-in-family,以此类推。

      relationship = tf.feature_column.categorical_column_with_vocabulary_list('relationship', ['Husband', 'Not-in-family', 'Wife', 'Own-child', 'Unmarried', 'Other-relative']
      # 哈希列 如果某一列类别中我不清楚一共有多少个唯一值  可以用哈希列, bucaket数量越大越好
      occupation = tf.feature_column.categorical_column_with_hash_bucket('occupation', hash_bucaket_size=1000)
      
      # 感觉对于类别数量特别多的那种, 感觉可以用下面这种方式
      workclass = tf.feature_column.categorical_column_with_vocabulary_list('workclass', data['workclass'].unique())
      
      categorical_columns = [relationship, occupation, workclass]
      
  3. 模型训练和评估
    输入到train当中的train_input只是将函数名称放入,这个东西不能调用传入,那么如果原先的input_fn有参数呢?如何将原先的input_fn中参数取出,可以使用functool.partial方法。 partial方法使用

    用于创建一个偏函数,将默认参数包装一个可调用对象,返回结果也是可调用对象。
    偏函数可以固定住原函数的部分参数,从而在调用时更简单。 比如:int2 = functools.partial(int, base=8)

    import functools
    
    train_inpf = functools.partial(input_fn, train_file, num_epochs=2, shuffle=True, batch_size=64)
    test_inpf = functools.partial(input_fn, test_file, num_epochs=1, shuffle=True, batch_size=64)
    

    tf.estimator进行初始化训练评估:

    classifier = tf.estimator.LinearClassifier(feature_columns=numeric_columns + categorical_columns)
    classifier.train(train_inpf)
    result = classifier.evaluate(test_inpf)
    
    # result是一个字典格式, 里面包含评估指标
    for key, value in sorted(result.items()):
    	print('%s: %s' % (key, value))
    

    当然,发现这里还可以使用Lambda函数封装input_fn:

    classifier.train(input_fn=lambda: input_fn(rain_file, num_epochs=2, shuffle=True, batch_size=64))
    

4.4 tf.data与tf.feature_columns

4.4.1 tf.data

tf.data API可以轻松处理大量数据,不同的数据格式转换以及复杂转换。 tf.data API在TensorFlow中引入了两个新的抽象类:

  1. tf.data.Dataset: 表示一系列元素,其中每个元素包含一个或多个Tensor对象。
    1. 创建来源(Dataset.from_tensor_slices()), 以通过一个或多个tf.Tensor对象构建数据集 — 得有来源
    2. 应用转换(Dataset.batch()), 以通过一个或多个tf.data.Dataset对象构建数据集 — 数据格式转换map, repeat, batch, shuffle
    3. dataset如果用于tf.estimator, 必须是字典形式的feature,label
  2. tf.data.Iterator提供了从数据集中提取元素的主要方法。 Iterator.get_next()返回的操作会在执行时生成Dataset的下一个元素,并且此操作通常充当输入管道代码和模型之间的接口。 直接运行dataset是不行的,是取不到数据的,用这个东西才行。

    在这里插入图片描述
  • Dataset: 包含创建和转换数据集的方法基类。 可以通过该类从内存中的数据或python生成器初始化数据集
  • TextLineDataset: 从文本文件中读取行
  • TFRecordDataset: 从TFRecord文件中读取记录
  • FixedLengthRecordDataset: 从二进制文件中读取具有固定大小的记录
  • Iterator: 提供一次方位一个数据集元素的方法

使用机制:

在这里插入图片描述
tf.Data.from_tensor_slices, make_one_shot_iterator的具体使用,可以参考官方文档,下面整理下用这个东西如何构建输入数据。

首先,弄一个字典,字典的键是特征名称,字典的值是每一列特征值

features = {
    
    'fea1': np.array([1,2]), 'fea2': np.array([2.8, 3]) ....}
lanbel = np.array([2, 1])

def tain_input_fn(features, labels, batch_size):
	dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))
	dataset = dataset.shuffle(1000).repeat().batch(batch_size)
	return dataset

iterator = dataset.make_one_shot_iterator().get_next()

with tf.Sessions() as sess:   # 这样才能看结果
	print(sess.run(iterator))

这种方式会将features和labels数组作为tf.constant()指令嵌入到TensorFlow图中。 这样非常适合小型数据集,但会浪费内存,因为会多次复制数组的内容, tf.GraphDef协议缓冲区2GB上限,超出这个东西就会报错

读取csv,文本文件

filenames = ['1.txt', '2.txt']  # 可以有多个文本文件
dataset = tf.data.TextLineDataset(filenames)  # 迭代读取每个文本文件的每个样本

默认情况下,TextLineDataset会生成每个文件的每一行, 所以通常会使用Dataset.map()预处理数据。

当使用Dataset.map(), Dataset.flat_map()以及Dataset.filter()转换时,会对每个element应用一个function。

dataset1 = dataset1.map(lambda x: ...)

训练的数据集大小指定:
tf.data API提供了两种主要方式处理同一个数据的多个epoch, 要迭代数据集多个周期,最简单的方法使用Dataset.repeat()转换,例如10个周期:

dataset = dataset.map()   # 处理
datset =datset.shuffle(buffer_size=10000)   # 随机打乱, 会维持一个固定的大小缓冲, 并从该缓冲区统一随机选择下一个元素
dataset = dataset.repeat(10)   # 这里如果无参数,将无限次的重复
datset = dataset.batch(32)  

4.4.2 特征处理tf.feature_column

Estimator的feature_columns参数来指定模型的输入。 特征列在输入数据(有input_fn返回)与模型之间架起了桥梁。要创建特征列, 调用tf.feature_column模块的函数。
在这里插入图片描述
下面是一些函数,除了backetized_column外的函数,要么返回一个Categorical Column对象,要么返回一个Dense Column对象。

在这里插入图片描述
具体函数的作用看官方文档吧。这类别型的还有个做特征交叉的crossed_column。还有做嵌入列的embedding_column。 bucketized_column叫做分桶列,也就是将数值型特征分桶,变成类别特征。这个感觉会特别方便。

在这里插入图片描述
具体使用:

# 首先先指定原始特征为数值列
numeric_feature_column = tf.feature_column.numeric_column("Year")

# 然后, 按照边界[1960, 1980, 2000]将numeric_column进行bucket
bucketized_feature_column = tf.feature_column.bucketized_column(source_column=numeric_feature_column, boundaries=[1960, 1980, 2000])

如果想对某一列one-hot编码,用tf.feature_column.categorical_column_with_identify

在这里插入图片描述

具体使用:

identity_feature_column = tf.feature_column.categorical_column_with_identity(key='my_feature_b', num_buckets=4)

Hashed Column(哈希列)

  • 处理的示例都包含很少的类别。但当类别的数量特别大时,我们不可能为每个词汇或整数设置单独的类别,因为这将会消耗非常大的内存。对于此类情况,我们可以反问自己:“我愿意为我的输入设置多少类别?

    在这里插入图片描述
    具体使用如下:

    hashed_feature_column =
        tf.feature_column.categorical_column_with_hash_bucket(
            key = "some_feature",
            hash_bucket_size = 100) # The number of categories
    

4.5 文本分类案例再来熟练下tf.estimator的使用

这里是基于一个文本数据集再把Estimator的流程走一遍, 毕竟文本的处理方式和上面直接读CSV还是有些差别的,这里会用到embedding_column对数据进行embedding等。采用的数据集是IMDB电影评论数据集, 做的任务就是输入句子,输出是正面评论还是负面评论,一个二分类的任务。关于数据集,不在这里做过多介绍,这个数据集Keras里面集成了,可以直接导入:

# 指定总共多少不同的词,每个样本的序列长度最大多少
vocab_size = 5000
sentence_size = 200
imdb = keras.datasets.imdb

(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=vocab_size)  # 词的个数,也就是词典的大小
# 这样获取的x_train是25000个list,每个list是一段文本, 里面的每个元素是单词在上面5000个字里面的下标
# 每段文本是不一样长的,所以下面需要进行序列的填充

# 序列填充
x_train_new = keras.proprecessing.sequnece.pad_sequences(x_train, maxlen=sentence_size, padding='post', value=0)
x_test_new = keras.proprecessing.sequence.pad_sequences(x_test, maxlen=setence_size, padding='post', value=0)

下面就是写输入函数:

def parse(x, y):
	features = {
    
    "x", x}
	return features, y

def train_input_fn():
	dataset = tf.data.Dataset.from_tensor_slices((x_train_new, y_train))
	dataset = dataset.shuffle(buffer_size=len(x_train_new))
	dataset = dataset.batch(100)
	dataset = dataset.map(parser)
	dataset = dataset.repeat()
	iterator = dataset.make_one_shot_iterator()
	return iterator.get_next()

def eval_input_fn():
	dataset = tf.data.Dataset.from_tensor_slices((x_test_new, y_test))
	dataset = dataset.batch(100)
	dataset = dataset.map(parser)
	dataset = dataset.repeat()
	iterator = dataset.make_one_shot_iterator()
	return iterator.get_next()

模型的输入列指定

# 这里可以看看embedding是怎么做的
column = tf.feature_column.categorical_with_identify('feature', vocab_size)  # one-hot
embedding_size = 50
word_embedding_column = tf.feature_column.embedding_column(column, dimension=embedding_size)

进行模型训练:这里可以指定模型的神经网络神经元的数量,以及几层, 特征列, 模型输出目录:

classifier = tf.estimator.DNNClassifier(
	hidden_units = [100, 50, 20],   # 3层网络层
	feature_columns = [word_embedding_column],
	model_dir = './tmp/embeddings'         # 模型保存路径, 有了这个之后,保存完了之后是可以通过TensorBoard直接观察的
)

classifier.train(input_fn=train_input_fn, steps=25000)
eval_results = classifier.evalueate(input_fn=eval_input_fn)
print(eval_results)

如果想用TensorBoard看模型的训练过程, 这里需要在远程进入tmp/embeddings目录,然后再里面输入命令:

tensorboard --logdir="./"

然后就可以在相应网址上看模型的训练效果,计算图等。因为这里面会有个events文件。TensorBoard观察的就是这么个东西。

5. 小总

这篇文章主要是整理的一些基础性的知识了,顺便学习了一波TensorFlow2.0里面的Estimator,之前竟然不知道这么个东西,用TensorFlow2.0大部分都是用的keras的一些函数, 这次又学习到了哈哈,原来还有这么方便的东西可以用,这以后要是搭建简单的全连接层的话,就直接用这个就好啦,w&D也可以用,下一篇文章会具体介绍。 下面依然是一张导图把知识拎起来:
在这里插入图片描述

参考:

猜你喜欢

转载自blog.csdn.net/wuzhongqiang/article/details/114945735