数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
#!/usr/bin/env sh
set -e
# set -e 是 Shell对调用命令返回值的处理方式。只要有一个不为零,就退出。
TOOLS=./build/tools
$TOOLS/caffe train \
--solver=examples/cifar10/cifar10_quick_solver.prototxt $@

# $@ 是所有参数
# reduce learning rate by factor of 10 after 8 epochs
$TOOLS/caffe train \
--solver=examples/cifar10/cifar10_quick_solver_lr1.prototxt \
--snapshot=examples/cifar10/cifar10_quick_iter_4000.solverstate $@
人工智能
2019-03-05 13:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言 只有光头才能变强。 文本已收录至我的GitHub仓库,欢迎Star: https://github.com/ZhongFuCheng3y/3y
回顾前面: 从零开始学TensorFlow【01-搭建环境、HelloWorld篇】
TensorFlow是什么意思?Tensor?Flow?这篇文章介绍TensorFlow一些最基础的知识。
一、Tensor介绍
在介绍之前,首先要记住一个结论: TensorFlow使用Tensor来表示数据
接着我们来看看 什么是Tensor ,在官网的文档中,Tensor被翻译成” 张量 “。其中也给出了一个定义: 张量 是对矢量和矩阵向潜在的更高维度的泛化,TensorFlow 在内部将张量表示为 基本数据类型的n维数组 。
不知道你们看完这句话有啥感受,反正我当时就看不懂,啥是”张量“?。于是,我就跑去知乎里边用 关键字 搜了一下:”张量是什么“。果真给我搜到了相关的问题:《怎么通俗地理解张量?》 https://www.zhihu.com/question/23720923
我本以为通过知乎,就可以通俗易懂地理解什么是张量,能给我一个清晰的认识。殊不知,大多数答主都在回答在 物理和数学 中张量的定义,随后贴出了一堆我看不懂的公式。其中,也看到了一种 相对 通俗易懂的定义: 一个量, 在不同的参考系下按照某种特定的法则进行变换, 就是张量.
把所有答主的回答都阅读了一遍,看完就 更加抽象了 。再回到官方文档中,看看官方介绍张量的例子, 貌似 有点懂了。
目前为止我们有两个结论: TensorFlow使用Tensor来表示数据 TensorFlow 在内部将张量表示为 基本数据类型的n维数组
我再翻译一下上面的两句话: 在TensorFlow所有的数据都是一个n维的数组,只是我们给它起了个名字叫做张量(Tensor) 中间折腾了一大堆,实际上还是将最开头的结论和官方的定义 再翻译 成自己觉得好理解的话...但很多时候,学习就这么一个过程。
1.1Tensor的基础
从上面我们已经得知,Tensor(张量)实际上就是一个n维的数组。这就延伸了几个的术语: 阶(秩) 形状
1.1.1阶(秩)
其实上,阶就是平时我们所说的 维数 。 比如我们有一个二维的数组,那么这个阶就是2 比如我们有一个三维的数组,那么这个阶就是3
以前在写Java的时候,可能一般接触到的都是二维的,但在机器学习上就很可能有很高的维度,那维数我们怎么数?很简单,我们 数括号 就行了。举个例子,我们可能会看到有下面的一个数组输出形式: [[[9 6] [6 9] [8 8] [7 9]] [[6 1] [3 5] [1 7] [9 4]]]
我们直接看第一个括号到第一个数字,有多少个括号就知道了。 [[[9 可以发现有3个括号,那这个就是一个三维的数组,它的阶(秩)就是3
1.1.2形状
张量的 形状 可以让我们看到 每个维度中元素的数量 。
比如我们在Java中创建出一个二维的数组: int [][] array = new int[3][4] ,我们就可以知道这个数组 有三行有四列 。但如果我们创建出一个多维的数组,单单只用行和列就描述不清了。所以,在TensorFlow一般我们会这样描述: 在维度一上元素的个数有3个,在维度二上元素的个数有4个。 其实说到底还是一个意思,但只是 说法 变了而已。
如果我们要打印上面数组的形状时,我们可以得到这样的结果: shape = (3,4) 。我们再看看第一篇写”机器学习HelloWorld“的时候,再来看看当时打印的结果: shape = (60000, 28, 28) 。通过shape我们就可以 得到一些信息 : 当前数组是三维的 在第一维中有60000个元素 在第二维中有28个元素 在第三维中有28个元素
那我们如果拿到一个数组,怎么通过 肉眼 看他的shape呢?
比如说: m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ,这个很简单,一眼就可以看出这个是一个二维数组(矩阵),有三行三列。所以shape的结果应该是(3,3)
再来看一个: t = [[[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]] ,从多个括号上我们可以看出,这是三维的。我们先把 最外层括号 去掉得到的结果是 [[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]
Ok,到这一步,我们可以理解成有 三 个子数组,于是我们的shape可以先写成 shape(3,?,?) 我们从括号上判断一定是三维的,所以肯定是 (?,?,?) 的。从“子数组”的个数我们将第一个“?”号填充为3
随后,我们继续把外层的括号去除,得到这样的结果: [2], [4], [6] ,也是有三个元素,于是我们的shape就可以填成 shape(3,3,?)
最后,再把括号去掉,我们可以发现只有一个元素,于是最后的结果就是 shape(3,3,1)
我们可以看下图来 巩固 一下上面所说的概念:
1.1.3 Tensor数据类型
TensorFlow 在内部将张量表示为 基本数据类型 的 n维数组,没错的。在一个数组里边,我们总得知道我们的存进去的数据究竟是什么 类型 。 我们可以将任意数据结构序列化为 string 并将其存储在 tf.Tensor 中。通过 tf.cast 可以将 tf.Tensor 从一种数据类型转型为另一种。
Tensor的数据类型如下所示:
二、特殊的张量
特殊的张量由一下几种: tf.Variable — 变量 tf.constant — 常量 tf.placeholder —占位符 tf.SparseTensor —稀疏张量
这次,我们先来讲讲前三种(比较好理解),分别是变量、常量和占位符。
2.1 常量
常量就是常量的意思, 一经创建就不会被改变 。(相信大家还是能够理解的)
在TensorFlow中,创建常量的方式十分简单: a = tf.constant(2) b = tf.constant(3)
2.2变量
变量也挺好理解的(就将编程语言的概念跟这里类比就好了)。一般来说,我们在 训练过程中的参数 一般用变量进行存储起来,因为我们的参数会不停的变化。
在TensorFlow创建变量有 两种 方式: # 1.使用Variable类来创建 # tf.random_normal 方法返回形状为(1,4)的张量。它的4个元素符合均值为100、标准差为0.35的正态分布。 W = tf.Variable(initial_value=tf.random_normal(shape=(1, 4), mean=100, stddev=0.35), name="W") b = tf.Variable(tf.zeros([4]), name="b") # 2.使用get_variable的方式来创建 my_int_variable = tf.get_variable("my_int_variable", [1, 2, 3], dtype=tf.int32, initializer=tf.zeros_initializer)
值得注意的是: 当我们创建完变量以后,我们每次使用之前,都需要为其进行初始化! tf.global_variables_initializer()
2.3占位符
我最早接触占位符这个概念的时候是在JDBC的时候。因为SQL 需要传入的参数才能确定下来 ,所以我们可能会写出这样的SQL语句: select * from user where id =?
同样地,在TensorFlow占位符也是这么一个概念,可能需要 等到运行的时候才把某些变量确定下来 ,于是我们就有了占位符。
在TensorFlow使用占位符也很简单: # 文件名需要等到运行的时候才确定下来 train_filenames = tf.placeholder(tf.string, shape=[None]) # ..省略一堆细节 # 运行的时候,通过feed_dict将占位符具体的值给确定下来 feed_dict={train_filenames: training_filenames}
上面的东西说白了在编程语言中都是有的, 只是语法变了而已 。
三、Flow?介绍图和节点
我们将Flow翻译成中文: 流 ,所以现在是 Tensor流 ?
其实,在TensorFlow中, 使用图 (graph) 来表示计算任务 。其实TensorFlow默认会给我们一张空白的 图 ,一般我们会叫这个为” 数据流图 “。数据流图由有向边和节点组成,在使用TensorFlow的时候我们会在图中创建各种的 节点 ,而Tensor会在这些节点中 流通 。所以,就叫做TensorFlow
那有人就会好奇,我们执行什么操作会创建节点呢?在TensorFlow中,节点的类型可以分为三种: 存储节点:有状态的变量操作,通常用于存储模型参数 计算节点:无状态的计算和控制操作,主要负责算法的逻辑或流程的控制 数据节点:数据的占位符操作,用于描述图外输入的数据
看到这里的同学,可能就反应过来了:原来在上面创建的变量、常量和占位符在TensorFlow中都会生成一个节点!对于这类的操作Operation(行为)一般大家会简说成 op
所以,op就是在TensorFlow中所执行的一个操作 统称 而已(有可能是创建变量的操作、也有可能是计算的操作)。在TensorFlow的常见的op有以下:
其实说白了就是TensorFlow会给我们一张空白的数据流图,我们往这张数据流图 填充 (创建节点),从而实现想要效果。 开局一张图,内容全靠编!
我们来看看官方的给出数据流图的gif,加深下印象。 TensorFlow使用数据流图来表示计算任务 TensorFlow使用Tensor来表示数据,Tensor在数据流图中流动。 在TensorFlow中”创建节点、运算“等行为统称为op
四、啥是session?
TensorFlow程序通常被组织成一个 构建阶段和执行阶段 . 在构建阶段, op的 执行步骤被描述成一个图 . 在执行阶段, 使用会话执行 执行图中的op。 注意:因为是有向边,所以 只有等到之前的入度节点们的计算状态完成后,当前节点才能执行操作 。
说白了,就是当我们在编写代码的时候,实际上就是在将TensorFlow给我们的 空白图 描述成一张我们 想要的图 。但我们想要运行出图的结果,那就必须通过session来执行。
举个小例子: import tensorflow as tf # 创建数据流图:y = W * x + b,其中W和b为存储节点,x为数据节点。 x = tf.placeholder(tf.float32) W = tf.Variable(1.0) b = tf.Variable(1.0) y = W * x + b # =========如果不使用session来运行,那上面的代码只是一张图。我们通过session运行这张图,得到想要的结果 with tf.Session() as sess: tf.global_variables_initializer().run() # Operation.run fetch = y.eval(feed_dict={x: 3.0}) # Tensor.eval print(fetch) # fetch = 1.0 * 3.0 + 1.0
4.1 Fetch是啥?
Fetch就时候可以在 session.run 的时候 传入多个op(tensor) ,然后 返回多个tensor (如果只传入一个tensor的话,那就是返回一个tensor)
4.2tensor.eval()和Operation.run()
有的同学在查阅资料的时候,发现可能调用的不是 session.run ,而是 tensor.eval()和Operation.run() 。其实,他们 最后的调用的还是 session.run 。不同的是 session.run 可以 一次 返回多个tensor(通过Fetch)。
最后
曾经看到一段话总结得不错: 使用 tensor 表示数据. 使用图 (graph) 来表示计算任务. 在会话(session)中运行图 通过 变量 (Variable) 维护状态. TensorFlow 是一个编程系统, 使用图来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或多个 Tensor , 执行计算, 产生 0 个或多个 Tensor . 每个 Tensor 是一个类型化的多维数组 .
这篇文章简单讲了TensorFlow是啥意思以及一些基础的概念。但我也只是简单 以我的理解方式 来说了一些常见概念。里头的知识点还是比较多的(比如创建变量的时候一般我们会指定哪些参数….),这些就交由大家去官网、博客、书籍去学习了。
我相信,只要了解了这些概念,那学习一定可以事半功倍!
下一篇TensorFlow文章敬请期待~
参考资料: https://juejin.im/post/5b345a49f265da599c561b25 https://github.com/geektime-geekbang/tensorflow-101/tree/master/notebook-examples 乐于输出 干货 的Java技术公众号:Java3y。公众号内有200多篇 原创 技术文章、海量视频资源、精美脑图,不妨来 关注 一下!
觉得我的文章写得不错,不妨点一下 赞 !
人工智能
2019-03-12 22:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
docker run -d --name test_name -p 8081:8081 $(cat /etc/hosts|awk -F ' ' '{if(NR>7){print "--add-host "$2":"$1}}') tomcat
#NR>7: 是将主机hosts文件中第7行后边的信息添加到容器的hosts文件中
人工智能
2019-03-12 14:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Debian9.8 安装 pytorch (不安装cuda):
apt install -y git python3-dev
git clone --recursive https://github.com/pytorch/pytorch
export NO_CUDA=1
export USE_NUMPY=1
export USE_FFMPEG=1
export USE_OPENCV=1
export USE_REDIS=1
cd pytorch
pip install opencv-python numpy scipy tensorflow scikit-learn matplotlib redis ffmpeg
pip install pyyaml
pip install -r requirements.txt
python3 setup.py install
pip install torchvision

或者直接按照pytorch官网上https://pytorch.org/选择你要安装的,如:
pip install https://download.pytorch.org/whl/cpu/torch-1.0.1-cp35-cp35m-win_amd64.whl

用命令查看pytorch适合安装成功:
pip list | grep torch

简单例子:
import torch
x = torch.empty(5, 3)
print(x)

输出:
哈哈,以后就可以用pytorch开发了
人工智能
2019-03-11 21:07:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
英特尔分布式深度学习平台Nauta-使用指南
Nauta 软件提供了多用户、分布式的计算环境,可以为运行深度学习模型的训练实验提供支持。实验结果可以通过命令行、web UI 和/或 TensorBoard来进行查看和监视。你可以使用已有数据集、自己的数据或在线下载数据,创建公共的或私有的目录来实现团队的协作。Nauta 使用产业领先的 Kubernetes 和 Docker 平台提供可伸缩和易于管理的能力。模版可以定制,从而减少多节点深度学习训练实验的复杂性,基于标准的容器环境就可以实现。为了测试你的模型,Nauta 同时支持批量和流式推理,在单一平台上就可以实现。
Nauta 客户端软件可以运行于以下操作系统: Ubuntu* (16.04, 18.04) RedHat* 7.5 macOS* High Sierra Windows* 10
Nauta 用户指南
描述了如何使用Nauta,关注于下面的一些主题: 基本概念-Basic Concepts 客户端安装-Client Installation and Configuration 快速开始-Getting Started 使用数据集-Working with Datasets 训练实验-Working with Experiments 使用模版-Working with Template Packs 实验求值-Evaluating Experiments 推理测试-Evaluating Experiments with Inference Testing 管理用户和资源-Managing Users and Resources 构建-Building Nauta CTL (nctl) CLI Commands config - 调整packs的配置为集群中可用的资源。 experiment - 训练和管理训练工作任务。 launch - 为Web UI 和 Tensorboard启动浏览器。 mount - 挂载用户目录相关的细节。 predict - 部署和管理训练模型的推理。 user - 添加/删除/列示系统用户。 verify - 验证 dlsctl 应用安装。 version - 显示 dlsctl 应用版本。
高级用法 安装更多的依赖 控制packs的参数
商标和授权
Copyright © 2019 Intel Corporation. All rights reserved.
Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and other countries.
* Other names and brands may be claimed as the property of others.
参考: 英特尔分布式深度学习平台Nauta-安装、配置与管理指南 英特尔为 Kubernetes 推出分布式深度学习平台:Nauta
人工智能
2019-03-03 09:51:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
英特尔分布式深度学习平台Nauta-安装、配置与管理指南
随着人工智能的发展,深度学习的价值不断增长,但实现它可能是一个复杂耗时的过程。英特尔(Intel)正寻求通过其在 Kubernetes 进行分布式深度学习的新开源平台来改变这一状况,该深度学习平台称为: Nauta 。

本指南包括 Nauta 安装、配置和管理,提供安装和配置Nauta的具体步骤。 同时包含了Nauta 要求、配置的选项,以及管理任务。
注意: 配置 Nauta 客户端的指南,参考 Nauta User Guide 。
Nauta 是一个软件套件,提供了多用户、分布式计算环境用于运行深度学习的模型训练试验。 实验结果可以查看和监视,可以通过命令行界面 (CLI)、Web UI 或 TensorBoard*来查看。你可以使用已有的数据集,自己的数据或者在线下载数据,创建共有或私有目录来在团队间协作。
Nauta 运行于 Kubernetes* 和 Docker*,易于管理,具有较好的伸缩性。Nauta 使用定制模版来消除创建和运行单个/多个节点深度学习的复杂性,在标准的容器环境中运行,不需要复杂的系统和脚本。
硬件需求
Nauta is intended to run on a multi-server Kubernetes cluster. To run Nauta, you will need at least one Master node, and one or more Worker nodes. Nauta is a platform for performing Deep Learning training, and requires robust hardware specifications to run with optimal performance.
安装概览
安装Nauta 在 'bare metal' (for example, non-cloud) 服务器环境,需要: 执行下面的命令: git clone --recursive https://github.com/IntelAI/nauta.git cd nauta 编译基本package (makefile实现自动化的系列处理,有一些需要的最小的 packages集合)。 发布 Nauta's inventory 文件告诉master 和 worker nodes在哪里,以及如何访问。 配置Nauta's 配置文件,告诉关于 proxies, network quirks 和 filesystem 偏好等。 运行安装脚本。
该过程包括: 创建 Kubernetes cluster, 所有的需要的 Docker files用于运行 Tensorflow*, Jupyter*, Tensorboard, 和 Horovod*。 安装 Nauta server-side 应用到新的Kubernetes cluster,然后启动系统来运行。
完成上面的操作需要花一些时间,我们一步步来。
文档 Flow
该指南包含下面的主题: System Software Components Requisites Building Nauta Installer System Requirements Target Host Requirements Inventory Configuration Nauta Configuration (Variables) Installation Package Requirements Installating and Starting Nauta User Management Troubleshooting
更多: 英特尔为 Kubernetes 推出分布式深度学习平台:Nauta
人工智能
2019-03-03 09:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
参考
tensorflow-windows-wheel 我选择了:1.12.0\py37\GPU\cuda100cudnn73sse2
前置环境 Visual Studio 2017 Anaconda3 这两个我都安装的最新版。 Visual Studio 2017不装会导致安装CUDA时"Visual Studio Integration"组件安装失败,我在此处卡了几天,查了好多资料都无效。
GPU相关软件下载 Nvidai驱动 CUDA10.0: cuda-tookit下载 , 选择CUDA Toolkit 10.0 cuDNN: cuDNN下载 , 选择Download cuDNN v7.3.1 (Sept 28, 2018), for CUDA 10.0 tensorflow安装包: tensorflow-windows-wheel
安装 显卡驱动安装 安装CUDA 安装好之后的环境变量默认包含: CUDA_PATH:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0 CUDA_PATH_V10_0:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0 PATH: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\libnvvp C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\extras\CUPTI\libx64(这个需要自己添加) 安装cuDNN 解压cuDNN后得到bin、include、lib三个文件夹 将bin下的cudnn64_7.dll拷贝到:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin 将include下的cudnn.h拷贝到:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\include 将libd下的cudnn.lib拷贝到:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\libd
tensorflow-gpu环境配置 打开 创建环境 conda create -n tensorflow_gpu_1.12_py37 python=3.7 激活环境 conda activate tensorflow_gpu_1.12_py37 安装tensorflow-gpu pip install E:\Downloads\tensorflow_gpu-1.12.0-cp37-cp37m-win_amd64.whl 测试 进入python >>> import tensorflow as tf >>> hello = tf.constant('Hello, tensorflow!') >>> sess = tf.Session() 输出: 2019-03-02 09:37:52.875467: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2 2019-03-02 09:37:53.200917: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with properties:name: GeForce RTX 2070 major: 7 minor: 5 memoryClockRate(GHz): 1.62 pciBusID: 0000:01:00.0 totalMemory: 8.00GiB freeMemory: 6.59GiB 2019-03-02 09:37:53.207587: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1511] Adding visible gpu devices: 0 2019-03-02 09:40:52.845289: I tensorflow/core/common_runtime/gpu/gpu_device.cc:982] Device interconnect StreamExecutor with strength 1 edge matrix: 2019-03-02 09:40:52.848702: I tensorflow/core/common_runtime/gpu/gpu_device.cc:988] 0 2019-03-02 09:40:52.850105: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1001] 0: N 2019-03-02 09:40:52.854219: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1115] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 6331 MB memory) -> physical GPU (device: 0, name: GeForce RTX 2070, pci bus id: 0000:01:00.0, compute capability: 7.5) 接着执行 print(sess.run(hello)) 输出: b'Hello, tensorflow!' 关闭session sess.close()
至此结束,第一次感觉开发环境搭建不容易。
人工智能
2019-03-02 10:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
环境 win7+py3.7
工具:pyinstaller
1、安装pyinstaller,cmd --> pip install pyinstaller
2、安装完成后,打开cmd,输入命令:pyinstaller -F *.py(星号为py文件的全路径,如下图)
卸载 pip uninstall pyinstaller
版本查看 py -3.6 -m pip list

测试
vscode 编辑器安装
安装 pip install pyinstaller
打包1 没有全路径 D:\python\py_zhizhu>pyinstaller -F py_ex_file.py 52 INFO: PyInstaller: 3.4 54 INFO: Python: 3.7.2 54 INFO: Platform: Windows-7-6.1.7601-SP1 99 INFO: wrote D:\python\py_zhizhu\py_ex_file.spec 107 INFO: UPX is not available. 110 INFO: Extending PYTHONPATH with paths ['D:\\python\\py_zhizhu', 'D:\\python\\py_zhizhu'] 110 INFO: checking Analysis 111 INFO: Building Analysis because Analysis-00.toc is non existent 111 INFO: Initializing module dependency graph... 111 INFO: Initializing module graph hooks... 114 INFO: Analyzing base_library.zip ... 3178 INFO: running Analysis Analysis-00.toc 3200 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable required by c:
人工智能
2019-03-01 15:47:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
K-近邻(K-Nearest Neighbors, KNN)是一个非常简单的机器学习算法,很多机器学习算法书籍都喜欢将该算法作为入门的算法作为介绍。
KNN分类问题是找出一个数据集中与给定查询数据点最近的K个数据点。这个操作也成为KNN连接(KNN-join)。可以定义为:给定两个数据集R合S,对R中的每一个对象,我们希望从S中找出K个最近的相邻对象。
在数据挖掘中,R和S分别称为查询和训练(traning)数据集。训练数据集S表示已经分类的数据,而查询数据集R表示利用S中的分类来进行分类的数据。
KNN是一个比较重要的 聚类算法 ,在数据挖掘(图像识别)、生物信息(如乳腺癌诊断)、天气数据生成模型和商品推荐系统中有很多应用。
缺点:开销大。特别是有一个庞大的训练集时。正是这个原因,使用MapReduce运行该算法显得非常的有用。
1、KNN算法
1.1、KNN分类
KNN的中心思想是建立一个分类方法,使得对于将y(响应变量)与x(预测变量)关联的平滑函数f的形势没有任何的假设: $$ x = (x_{1},x_{2},...,x_{n}) $$
$$ y = f(x) $$
函数f是非参数化的,因为它不涉及任何形式的参数估计。在KNN中,给定一个新的点$p=(p_{1},p_{2},...,p_{n})$,要动态的识别训练集数据集中与p相似的K个观察(k个近邻)。近邻由一个距离或相似度来定义。可以根据独立变量计算不同观察之间的距离,我们采用欧氏距离进行计算: $$ \sqrt{(x_{1} - p_{1})^2 + (x_{2} - p_{2})^2 + ... + (x_{n}-p_{n})^2} $$ 关于距离的算法以及种类有很多,本章节我们采用欧氏距离,即坐标系距离计算方法。
那么如何找出k个近邻呢?
我们先计算出欧氏距离的集合,然后将这个查询对象分配到k个最近训练数据中大多数对象所在的类。
1.2、距离函数
假设有两个n维对象: $$ X = (X_{1},X_{2},...,X_{n}) $$
$$ Y = (Y_{1},Y_{2},...,Y_{n}) $$
$distance(X,Y)$可以定义如下: $$ distance(X,Y) = \sqrt{\sum_{i=1}^{n}(x_{i}-y_{i})^2} $$ 注意欧氏距离只适用于连续性数值类型:double。如果是其他类型,则可以考虑关联业务情况下设置距离函数,将其转化为double类型。 关于所有的有关各种距离的介绍,参考博文:
1.3、KNN解析
KNN算法是一种对未分类数据进行分类的直观方法,他会根据未分类数据与训练数据集中的数据的相似度或距离完成分类。在下面的例子中,我们有4个分类$C_{1} - C_{4}$:
可以看到,我们的K=6,因此选取了6个近邻,在这6个近邻中,出现在上方的那个类中有4个属于它的点,因此,我们将P点归为上方圆圈包含的这一类型中。
1.4、算法描述
KNN算法可以总结为以下的步骤: 确定K 计算新输入与所有训练集之间的距离 对距离排序,并根据第k个最小距离确定k个近邻 手机这些近邻所属的类别 根据多数投票确定类别
算法复杂度:$O(N^2)$
2、Spark实现
2.1、形式化描述
设R和S是d维数据集,我们想找出其kNN(RS)。进一步假设所有训练数据(S)已经分类到$C={C_{1},C_{2},...,C_{n}}$,这里$C$表示所有可能的分类。R、S和C的定义如下: $$ R = {R_{1},R_{2},...,R_{n}} $$
$$ S = {S_{1},S_{2},...,S_{n}} $$
$$ C = {C_{1},C_{2},...,C_{n}} $$
在这里: $R_{i} = (r_{i},a_{1},a_{2},...,a_{n})$,其中$r_{i}$是当前记录的ID,$a_{1},...,a_{n}$是$R_{i}$的属性; $S_{j} = {r_{j},b_{1},b_{2},...,b_{n}}$同上。 $C_{j}$是$S_{j}$的分类标识符。
我们的目标是找出$KNN(R,S)$。
2.2、数据集
S数据集如下所示: 100;c1;1.0,1.0 101;c1;1.1,1.2 102;c1;1.2,1.0 103;c1;1.6,1.5 104;c1;1.3,1.7 105;c1;2.0,2.1 106;c1;2.0,2.2 107;c1;2.3,2.3 208;c2;9.0,9.0 209;c2;9.1,9.2 210;c2;9.2,9.0 211;c2;10.6,10.5 212;c2;10.3,10.7 213;c2;9.6,9.1 214;c2;9.4,10.4 215;c2;10.3,10.3 300;c3;10.0,1.0 301;c3;10.1,1.2 302;c3;10.2,1.0 303;c3;10.6,1.5 304;c3;10.3,1.7 305;c3;1.0,2.1 306;c3;10.0,2.2 307;c3;10.3,2.3
其中,第一列为每条记录的唯一ID,第二列为该条记录的所属类别,之后的都为维度信息;
R数据集的信息如下: 1000;3.0,3.0 1001;10.1,3.2 1003;2.7,2.7 1004;5.0,5.0 1005;13.1,2.2 1006;12.7,12.7
其中,第一列为每条记录的唯一ID,之后的都为维度信息;
接下来我们使用KNN算法,来计算R数据集中每个记录所属的类别。
2.3、Spark实现 package com.sunrun.movieshow.autils.knn; import com.google.common.base.Splitter; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.api.java.function.Function; import org.apache.spark.broadcast.Broadcast; import scala.Tuple2; import java.util.*; public class KNNTester { /** * 1. 获取Spark 上下文对象 * @return */ public static JavaSparkContext getSparkContext(String appName){ SparkConf sparkConf = new SparkConf() .setAppName(appName) //.setSparkHome(sparkHome) .setMaster("local[*]") // 串行化器 .set("spark.serializer","org.apache.spark.serializer.KryoSerializer") .set("spark.testing.memory", "2147480000"); return new JavaSparkContext(sparkConf); } /** * 2. 将数字字符串转换为Double数组 * @param str 数字字符串: "1,2,3,4,5" * @param delimiter 数字之间的分隔符:"," * @return Double数组 */ public static List transferToDoubleList(String str, String delimiter){ // 使用Google Splitter切割字符串 Splitter splitter = Splitter.on(delimiter).trimResults(); Iterable tokens = splitter.split(str); if(tokens == null){ return null; } List list = new ArrayList<>(); for (String token : tokens) { list.add(Double.parseDouble(token)); } return list; } /** * 计算距离 * @param rRecord R数据集的一条记录 * @param sRecord S数据集的一条记录 * @param d 记录的维度 * @return 两条记录的欧氏距离 */ public static double calculateDistance(String rRecord, String sRecord, int d){ double distance = 0D; List r = transferToDoubleList(rRecord,","); List s = transferToDoubleList(sRecord,","); // 若维度不一致,说明数据存在问题,返回NAN if(r.size() != d || s.size() != d){ distance = Double.NaN; } else{ // 保证维度一致之后,计算欧氏距离 double sum = 0D; for (int i = 0; i < s.size(); i++) { double diff = s.get(i) - r.get(i); sum += diff * diff; } distance = Math.sqrt(sum); } return distance; } /** * 根据(距离,类别),找出距离最低的K个近邻 * @param neighbors 当前求出的近邻数量 * @param k 寻找多少个近邻 * @return K个近邻组成的SortedMap */ public static SortedMapfindNearestK(Iterable> neighbors, int k){ TreeMap kNeighbors = new TreeMap<>(); for (Tuple2 neighbor : neighbors) { // 距离 Double distance = neighbor._1; // 类别 String classify = neighbor._2; kNeighbors.put(distance, classify); // 如果当前已经写入K个元素,那么删除掉距离最远的一个元素(位于末端) if(kNeighbors.size() > k){ kNeighbors.remove(kNeighbors.lastKey()); } } return kNeighbors; } /** * 计算对每个类别的投票次数 * @param kNeighbors 选取的K个最近的点 * @return 对每个类别的投票结果 */ public static Map buildClassifyCount(Map kNeighbors){ HashMap majority = new HashMap<>(); for (Map.Entry entry : kNeighbors.entrySet()) { String classify = entry.getValue(); Integer count = majority.get(classify); // 当前没有出现过,设置为1,否则+1 if(count == null){ majority.put(classify,1); }else{ majority.put(classify,count + 1); } } return majority; } /** * 根据投票结果,选取最终的类别 * @param majority 投票结果 * @return 最终的类别 */ public static String classifyByMajority(Map majority){ String selectedClassify = null; int maxVotes = 0; // 从投票结果中选取票数最多的一类作为最终选举结果 for (Map.Entry entry : majority.entrySet()) { if(selectedClassify == null){ selectedClassify = entry.getKey(); maxVotes = entry.getValue(); }else{ int nowVotes = entry.getValue(); if(nowVotes > maxVotes){ selectedClassify = entry.getKey(); maxVotes = nowVotes; } } } return selectedClassify; } public static void main(String[] args) { // === 1.创建SparkContext JavaSparkContext sc = getSparkContext("KNN"); // === 2.KNN算法相关参数:广播共享对象 String HDFSUrl = "hdfs://10.21.1.24:9000/output/"; // k(K) Broadcast broadcastK = sc.broadcast(6); // d(维度) Broadcast broadcastD = sc.broadcast(2); // === 3.为查询和训练数据集创建RDD // R and S String RPath = "data/knn/R.txt"; String SPath = "data/knn/S.txt"; JavaRDD R = sc.textFile(RPath); JavaRDD S = sc.textFile(SPath); // // === 将R和S的数据存储到hdfs // R.saveAsTextFile(HDFSUrl + "S"); // S.saveAsTextFile(HDFSUrl + "R"); // === 5.计算R&S的笛卡尔积 JavaPairRDD cart = R.cartesian(S); /** * (1000;3.0,3.0,100;c1;1.0,1.0) * (1000;3.0,3.0,101;c1;1.1,1.2) */ // === 6.计算R中每个点与S各个点之间的距离:(rid,(distance,classify)) // (1000;3.0,3.0,100;c1;1.0,1.0) => 1000 is rId, 100 is sId, c1 is classify. JavaPairRDD> knnPair = cart.mapToPair(t -> { String rRecord = t._1; String sRecord = t._2; // 1000;3.0,3.0 String[] splitR = rRecord.split(";"); String rId = splitR[0]; // 1000 String r = splitR[1];// "3.0,3.0" // 100;c1;1.0,1.0 String[] splitS = sRecord.split(";"); // sId对于当前算法没有多大意义,我们只需要获取类别细信息,即第二个字段的信息即可 String sId = splitS[0]; // 100 String classify = splitS[1]; // c1 String s = splitS[2];// "3.0,3.0" // 获取广播变量中的维度信息 Integer d = broadcastD.value(); // 计算当前两个点的距离 double distance = calculateDistance(r, s, d); Tuple2 V = new Tuple2<>(distance, classify); // (Rid,(distance,classify)) return new Tuple2<>(rId, V); }); /** * (1005,(2.801785145224379,c3)) * (1006,(4.75078940808788,c2)) * (1006,(4.0224370722237515,c2)) * (1006,(3.3941125496954263,c2)) * (1006,(12.0074976577137,c3)) * (1006,(11.79025020938911,c3) */ // === 7. 按R中的r根据每个记录进行分组 JavaPairRDD>> knnGrouped = knnPair.groupByKey(); // (1005,[(12.159358535712318,c1),....,(7.3171032519706865,c3), (7.610519036176179,c3)]), // (1000,[(2.8284271247461903,c1), (2.6172504656604803,c1), (2.690724....]) // === 8.找出每个R节点的k个近邻 JavaPairRDD knnOutput = knnGrouped.mapValues(t -> { // K Integer k = broadcastK.value(); SortedMap nearestK = findNearestK(t, k); // {2.596150997149434=c3, 2.801785145224379=c3, 2.8442925306655775=c3, 3.0999999999999996=c3, 3.1384709652950433=c3, 3.1622776601683795=c3} // 统计每个类别的投票次数 Map majority = buildClassifyCount(nearestK); // {c3=1, c1=5} // 按多数优先原则选择最终分类 String selectedClassify = classifyByMajority(majority); return selectedClassify; }); // 存储最终结果 knnOutput.saveAsTextFile(HDFSUrl + "/result"); /** * [root@h24 hadoop]# hadoop fs -cat /output/result/p* * (1005,c3) * (1001,c3) * (1006,c2) * (1003,c1) * (1000,c1) * (1004,c1) */ } }
步骤7和8也可以通过reduceByKey或者CombineByKey进行一步到位。先来看看我们的转换过程: RDD: —— knnPair: JavaPairRDD> —— knnGrouped: JavaPairRDD>> —— knnOutput:JavaPairRDD
变换过程: knnPair => groupBy => knnGrouped knnGrouped => mapValues => knnOutput
显然,我们无法使用reduceByKey,因此他要求输出类型等同于输入类型。聚集的返回类型不同于聚集值的类型时就要使用combineByKey变换。因此,我们将使用combineByKey把步骤7和8合并到一起。这个合并步骤如下:
RDD: —— knnPair: JavaPairRDD> —— knnOutput: JavaPairRDD
变换过程: —— knnPair => combineByKey => knnOutput
人工智能
2019-03-01 15:09:02
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
基于电影的评分的推荐系统,本章节使用三个相似度算法,求解最佳电影的推荐。
1、测试数据集
user1 movie1 1 user1 movie2 2 user1 movie3 3 user2 movie1 1 user2 movie2 3 user2 movie2 3 user2 movie5 3
每一条记录代表用户对某个电影的评分,采用5分制度。
2、Spark实现 public Map, Tuple3> startCompute(String inputFile) throws Exception { HashMap returnData = new HashMap<>(); // handler file String sparkFile = CommonUtils.handlerFile(inputFile); // 3.Get input file DataSet. JavaRDD firstRDD = sc.textFile(inputFile); System.out.println("====== debug1: line: K=V=line ======"); firstRDD.foreach(line -> System.out.println("debug 1: " + line)); // 4. 找出谁对电影进行了评论:Tuple(movie,Tuple(user,score)) JavaPairRDD> moviesRDD = firstRDD.mapToPair( line -> { // 解析列 String[] tokens = line.split("\t"); String user = tokens[0]; String movie = tokens[1]; Integer score = new Integer(tokens[2]); // 返回Tuple(movie,Tuple(user,score)) return new Tuple2>(movie, new Tuple2<>(user, score)); } ); System.out.println("====== debug2: moviesRDD: K = , V = Tuple2 ======"); moviesRDD.foreach( debug2 -> { System.out.println("debug2: key = " + debug2._1 + "\t value = " + debug2._2); }); // 5.按movie对movieRDD进行分组:Tuple2>> JavaPairRDD>> moviesGroupRDD = moviesRDD.groupByKey(); System.out.println("===== debug3: moviesGroupRDD: K = , V = Iterable> ======"); moviesGroupRDD.foreach(debug3 -> { System.out.println("debug2: key = " + debug3._1 + "\t" + debug3._2); }); // 6.找出每个电影的评分数量(即评论人数):Tuple2> JavaPairRDD> usersRDD = moviesGroupRDD.flatMapToPair(mg -> { // 当前处理的电影 String movie = mg._1; // 统计评分人数 int countScores = 0; // 从同一个电影对应的多个组中收集所有用户-评分信息(user,score) List> listUserAndScores = new ArrayList<>(); // (user,score) for (Tuple2 userAndScore : mg._2) { countScores++; // put to list. listUserAndScores.add(userAndScore); } // 返回结果 List(>) List>> result = new ArrayList<>(); for (Tuple2 listUserAndScore : listUserAndScores) { String user = listUserAndScore._1; Integer score = listUserAndScore._2; // 组合为T3(movie,score,numberOfScores) Tuple3 t3 = new Tuple3<>(movie, score, countScores); // 写入结果 result.add(new Tuple2<>(user, t3)); } // iterator扁平化 return result.iterator(); }); System.out.println("===== debug4: usersRDD: K = , V = Tuple3 ======"); usersRDD.foreach(u -> { System.out.println("debug4: key = " + u._1 + "\t" + "value = " + u._2); }); // 7.进行自连接:以user为键 JavaPairRDD, Tuple3>> joinRDD = usersRDD.join(usersRDD); System.out.println("===== debug5: joinRDD: K = , V = Tuple2,Tuple3> ======"); joinRDD.foreach(join -> { System.out.println("debug5: key = " + join._1 + "\t" + "value = " + join._2); }); // 8.删除重复的(movie1,movie2)对,确保对于任意的键值对,都有movie1 < movie2 JavaPairRDD, Tuple3>> filterJoinRDD = joinRDD.filter(j -> { // (movie, score, numberOfScore) Tuple3 v1 = j._2._1; Tuple3 v2 = j._2._2; // get movie name String movieName1 = v1._1(); String movieName2 = v2._1(); if (movieName1.compareTo(movieName2) < 0) { return true; } else { return false; } }); System.out.println("===== debug6: filterJoinRDD: K = , V = Tuple2,Tuple3> ======"); filterJoinRDD.foreach(filterJoin -> { System.out.println("debug6: key = " + filterJoin._1 + "\t" + "value = " + filterJoin._2); }); // 9.生成所有的(movie1,movie2)组合 JavaPairRDD< Tuple2, Tuple7> moviePairsRDD = filterJoinRDD.mapToPair(fj -> { // key = user去掉 // String user = fj._1; // 获得两个电影的信息: Tuple(movie,score,numberOfScores) Tuple3 m1 = fj._2._1; Tuple3 m2 = fj._2._2; // 组合两个电影的名字,作为返回的键 Tuple2 m1m2Key = new Tuple2<>(m1._1(), m2._1()); // 获取两个电影的分数计算系数,作为返回的V Tuple7 value = new Tuple7<>( m1._2(), //m1 score m1._3(), //m1 hot(number of m1 scores) m2._2(), // m2 score m2._3(), // m2 hot(number of m2 scores) m1._2() * m2._2(), // m1.score * m2.score m1._2() * m1._2(), // square of m1.score m2._2() * m2._2() // square of m2.score ); return new Tuple2<>(m1m2Key, value); }); System.out.println("===== debug7: moviePairsRDD: K = Tuple, V = ======"); moviePairsRDD.foreach(moviePair -> { System.out.println("debug7: key = " + moviePair._1 + "\t" + "value = " + moviePair._2); }); // 10.电影对分组: (movie1,movie2) -> list() JavaPairRDD, Iterable>> moviePairGroupRDD = moviePairsRDD.groupByKey(); System.out.println("===== debug8: moviePairs: K = Tuple, V = ====== ======"); moviePairGroupRDD.foreach(moviePairGroup -> { System.out.println("debug8: key = " + moviePairGroup._1 + "\t" + "value = " + moviePairGroup._2); }); // end.计算相似度数据 JavaPairRDD, Tuple3> correlations = moviePairGroupRDD.mapValues(value -> { return CalculateSimilarity.calculateCorRelation(value); }); System.out.println("====== Movie Similarity result is:(pearson,cosine,jaccard) ======"); correlations.mapToPair(c ->{ String key = "(" + c._1._1 + "," + c._1._2 + ")"; return new Tuple2<>(key, c._2); }).sortByKey().foreach(r -> { System.out.println(r._1 + "\t => \t" + r._2); }); return correlations.collectAsMap(); }
人工智能
2019-03-01 15:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
闲鱼技术-上叶,仝辉,深宇
开篇:
  在《UI2CODE--整体设计》篇中,我们提到UI2CODE工程的第一步是版面分析,如果是白色的简单背景,我们可以像切西瓜一样,将图片信息切割为GUI元素。但是在实际生产过程中,UI的复杂度会高很多。本篇我们将围绕版面分析这块内容分享我们的探索过程。
架构设计:
  版面分析主要处理UI图像的前景提取和背景分析:
通过前后景分离算法,将UI视觉稿剪裁为GUI元素: 背景分析:通过机器视觉算法,分析背景颜色,背景渐变方向,以及背景的连通区域。 前景分析:通过深度学习算法,对GUI碎片进行整理,合并,识别。
背景分析:
  背景分析的关键在于找到背景的连通区域和闭合区间;
  我们从一个实际的应用场景来分析整个背景提取的过程:
我们期望将上一张图片,通过UI2CODE,来提取GUI元素。
第一步:判断背景区块,通过sobel,Lapacian,canny等边缘检测的方法计算出梯度变化方向,从而得到纯色背景和渐变色背景区域。
基于拉普拉斯算子的背景区域提取
离散拉普拉斯算子的模板:

扩展模板:

当该区域点与模板点乘,得到的数值越小越是平坦的区域,即接近背景区域。如果是渐变色区域,则统计它的运动趋势。(左->右,右->左,上->下, 下->上)
提取效果如下:
我们发现上图虽然能大致提取背景轮廓,但是对于有渐变色的背景还是比较粗糙。
因此我们通过统计背景运动趋势的方式来判定它是否存在渐变色背景。如果存在,我们将通过第二步进行精细化处理。
第二步:基于漫水填充算法,选取漫水的种子节点,滤除渐变色背景区域噪声。 def fill_color_diffuse_water_from_img(task_out_dir, image, x, y, thres_up = (10, 10, 10), thres_down = (10, 10, 10), fill_color = (255,255,255)): """ 漫水填充:会改变图像 """ # 获取图片的高和宽 h, w = image.shape[:2] # 创建一个h+2,w+2的遮罩层, # 这里需要注意,OpenCV的默认规定, # 遮罩层的shape必须是h+2,w+2并且必须是单通道8位,具体原因我也不是很清楚。 mask = np.zeros([h + 2, w + 2], np.uint8) # 这里执行漫水填充,参数代表: # copyImg:要填充的图片 # mask:遮罩层 # (x, y):开始填充的位置(开始的种子点) # (255, 255, 255):填充的值,这里填充成白色 # (100,100,100):开始的种子点与整个图像的像素值的最大的负差值 # (50,50,50):开始的种子点与整个图像的像素值的最大的正差值 # cv.FLOODFILL_FIXED_RANGE:处理图像的方法,一般处理彩色图象用这个方法 cv2.floodFill(image, mask, (x, y), fill_color, thres_down, thres_up, cv2.FLOODFILL_FIXED_RANGE) cv2.imwrite(task_out_dir + "/ui/tmp2.png", image) # mask是非常重要的一个区域,这块区域内会显示哪些区域被填充了颜色 # 对于UI自动化,mask可以设置成shape,大小以1最大的宽和高为准 return image, mask
处理过后的效果如下:
第三步:通过版面切割,提取GUI元素,切割方法之前有提到过。
这个时候我们已经成功提取了80%的GUI元素,但是叠加图层部分元素还无法解析。
第四步:通过霍夫直线、霍夫圆和轮廓查找的方式找到对称的轮廓区域。对提取的轮廓区域做第二步到第四步的递归处理。

复杂背景的提取是后续前景分析的基础。当提炼出背景区域后,我们就可以通过连通域分析前景区域,并利用组件识别的方式分析前景类别,再通过语义分析等方式对前景做拆分和合并。
背景分析小结:
对比较简单的渐变色背景,通过上述四步基本都能够解决。基于霍夫直线和霍夫圆的思想,去查找特定的轮廓区域,并分析这些轮廓区域做细化分析。再通过漫水填充的方式消除渐变色背景。
对于复杂背景的处理,可以基于目标检测的方法,找到一些特定含义的内容。再利用马尔科夫随机场(mrf)细化边缘特征。
前景分析
前景分析的关键在于组件完整性切割与识别;
我们通过连通域分析,防止组件碎片化,再通过机器学习识别组件类型,再通过组件类型进行碎片化合并,反复执行上述动作,直到无特征属性碎片。
但是有些情况会比较复杂,我们通过瀑布流中提取一个完整item为例:
概述
闲鱼页面中瀑布流卡片识别是实现布局分析的一个重要步骤,需求是当卡片完整出现在截屏图像中时(允许图标遮挡)需要识别出来,卡片被背景部分遮挡时不应该识别出来。下图所示的瀑布流卡片样式,由于其布局紧凑且样式繁多,导致容易产生漏检或误检。
瀑布流卡片样式 a)红框显示卡片部分被图标遮挡 b)红框显示卡片内图片颜色和卡片外背景颜色接近
  基于边缘梯度或连通域的传统图像处理方法能根据图像本身的灰度或者形状特征提取出瀑布流卡片的轮廓,优点是定位精度高、运算速度快。缺点是容易受到干扰,召回率不高。
  基于目标检测或者特征点检测的深度学习方法采用有监督的方式学习卡片的样式特征,优点是不易受到干扰,召回率很高。缺点是因为涉及回归过程,定位精度比传统图像处理方法要低,并且需要大量的人工标注,运算速度也比传统图像处理方法要慢。
  受集成学习的启发,通过融合传统图像处理方法和深度学习方法,结合两者各自的优点,最终能得到具有较高精确率、召回率和定位精度的识别结果。
传统图像处理
1.算法流程
整个算法流程如下图所示:
1》. 输入的瀑布流卡片图像转换成灰度图后使用对比度受限的自适应直方图均衡化(CLAHE)进行增强
2》. 使用Canny算子进行边缘检测得到二值化图像
3》. 对二值化图像进行形态学膨胀处理,连接断开的边缘
4》. 提取连续边缘的外层轮廓,并基于轮廓包含区域的面积大小丢弃面积较小的轮廓,输出候选轮廓
5》. 使用Douglas-Peucker算法进行矩形逼近,保留最像矩形的外轮廓,输出新的候选轮廓
6》. 最后对第4步的候选轮廓进行水平和垂直方向投影得到平滑的轮廓作为输出
卡片轮廓提取流程
2.边缘检测
算法流程中1》-3》可以归为边缘检测部分。
受各种因素影响,图像会出现降质,需要对其进行增强来提高边缘检测的效果。使用全图单一的直方图进行均衡化显然不是最好的选择,因为截取的瀑布流图像不同区域对比度可能差别很大,增强后的图像可能会产生伪影。在单一直方图均衡化的基础上,学者基于分块处理的思想提出了自适应的直方图均衡化算法(AHE),但是AHE在增强边缘的同时有时候也会将噪声放大。后来学者在AHE的基础上提出了CLAHE,利用一个对比度阈值来去除噪声的干扰,如下图所示直方图,CLAHE不是将直方图中超过阈值的部分丢弃,而是将其均匀分布于其他bin中。
CLAHE对比度限制示意图
Canny算子是一种经典的边缘检测算子,它能得到精确的边缘位置。Canny检测的一般步骤为:1)用高斯滤波进行降噪 2)用一阶偏导的有限差分计算梯度的幅值和方向 3)对梯度幅值进行非极大值抑制 4)用双阈值检测和连接边缘。实验过程中,需要多次尝试选择较好的双阈值参数。
检测出来的边缘在某些局部地方会断开,可以采用特定形状和尺寸的结构元素对二值化图像进行形态学膨胀处理来连接断开的边缘。边缘检测的结果如下图所示,其中c)中CLAHE设定对比度阈值为10.0,区域大小为(10,10),d)中Canny检测设置双阈值为(20,80),e)中形态学膨胀结构元素使用大小为(3,3)的十字线。
边缘检测结果 a)原始图像 b)灰度化图像 c)CLAHE增强图像 d)Canny检测图像 e)形态学膨胀图像
3.轮廓提取
算法流程中4》-6》可以归为轮廓提取部分。
二值图像形态学膨胀处理后,首先提取连续边缘的外层轮廓。如下图所示,对于只有0和1的二值图像,假设S1为像素值为0的一堆背景点,S2为像素值为1的一堆前景点,外层轮廓B1为一堆前景点最外围的点,内层轮廓B2为一堆前景点最内部的点。通过对二值图像进行行扫描给不同轮廓边界赋予不同的整数值,从而确定轮廓的类型以及之间的层次关系。提取出外层轮廓后通过计算轮廓包含的面积大小丢弃面积较小的外层轮廓,输出第一步候选轮廓。
内外轮廓示意图
闲鱼页面瀑布流卡片轮廓近似于矩形,在四个角由弧形曲线连接。对于提取的候选轮廓使用Douglas-Peucker算法进行矩形逼近,保留形状接近矩形的外轮廓。Douglas-Peucker算法通过将一组点表示的曲线或多边形拟合成另一组更少的点,使得两组点之间的距离满足特定的精度。之后输出第二步候选轮廓。
通过对第二步候选轮廓所处位置对应的第一步候选轮廓进行水平和垂直方向投影,去除毛刺影响,输出矩形轮廓。轮廓提取的结果如下图所示,其中c)中轮廓包含面积设置的阈值为10000,d)中Douglas-Peucker算法设置的精度为0.01*轮廓长度。本文所有提取的轮廓均包含输入框。
轮廓提取结果 a)原始图像 b)形态学膨胀图像 c)红色线段为第一步候选轮廓 d)红色线段为第二步候选轮廓 e)红色线段为最终输出矩形轮廓
我们再介绍下机器学习如何处理:
深度学习算法
1.目标检测算法
传统算法中提出采用传统图像处理方法提取轮廓特征,这样会带来一个问题:当图像不清晰或者有遮挡的情况下无法提取出轮廓,即召回率不是很高。
基于卷积神经网络的目标检测算法能通过输入大量样本数据,学习到更具有代表性和区别性的特征。目前目标检测算法主要分成两个派系:以R-CNN家族为代表的两阶段流和以YOLO、SSD为代表的一阶段流。一阶段流直接对预测的目标进行分类和回归,优点是速度快,缺点是mAP整体上没有两阶段流高。两阶段流在对预测的目标进行分类和回归前需要先生成候选的目标区域,这样训练时更容易收敛,因此优点是mAP高,缺点是速度上不如一阶段流。不管是一阶段流还是两阶段流,通用的目标检测推理过程如图所示。输入一张图像到特征提取网络(可选择VGG、Inception、Resnet等成熟的CNN网络)得到图像特征,然后将特定区域特征分别送入分类器和回归器进行类别分类和位置回归,最后将框的类别和位置进行输出。
目标检测推理过程
2.Faster R-CNN
Faster R-CNN对R-CNN家族最大的贡献是将生成候选目标区域的过程整合到整个网络中,使得综合性能有了较大提高,尤其是在检测速度上。Faster R-CNN的基本结构如图所示。主要分为4个部分:1)conv layers。作为一种特征提取网络,使用一组基础的conv+relu+pooling层提取图像的特征,该特征被共享用于后续RPN网络和全连接层。2)Region Proposal Network。该网络用于生成候选目标框,通过softmax判断候选框是属于前景还是背景,并且通过候选框回归修正候选框位置。3)RoI pooling。收集输入的特征图和候选区域,将这些候选区域映射到固定大小并送入后续全连接层。4)classifer。计算候选框的具体类别,并且再次回归修正候选框的位置。
Faster R-CNN基本结构
使用Faster R-CNN进行瀑布流卡片的识别,得到下图的结果。
Faster R-CNN识别结果 a)原始图像 b)红框显示识别的卡片
传统算法与机器学习的融合
1.融合流程
描述的传统图像方法能够获得高定位精度的卡片位置,但受卡片本身模式的影响,召回率不高)中右边卡片没有检测到。上文描述的基于目标检测的深度学习方法具有较高的泛化能力,能获得较高的召回率,但是回归过程无法得到高定位精度的卡片位置)中卡片都能检测出来,但有两个卡片的边缘几乎粘连在了一起。
将两种方法得到的结果融合在一起,能同时获得高精确率、高召回率、高定位精度的检测结果。融合过程如下: 输入一张图像,并行运行传统图像处理方法和深度学习方法,分别得到提取的卡片框trbox和dlbox。定位精度以trbox为标杆,精确率和召回率以dlbox为标杆 筛选trbox。规则为当trbox与dlbox的IOU大于某个阈值时(比如0.8)保留此trbox,否则丢弃,得到trbox1 筛选dlbox。规则为当dlbox与trbox1的IOU大于某个阈值时(比如0.8)丢弃此dlbox,否则保留,得到dlbox1 修正dlbox1位置。规则为dlbox1的每条边移动到距离其最近的一条直线上,约束条件为移动的距离不能超过给定的阈值(比如20个像素),并且移动的边不能跨越trbox1的边,得到修正的dlbox2 输出trbox1+dlbox2为最终融合的卡片框
2.结果
先介绍几个基本指标:
True Positive(TP):被模型预测为正的正例数
True Negative(TN):被模型预测为负的负例数
False Positive(FP):被模型预测为正的负例数
False Negative(FN):被模型预测为负的正例数
精确率(Precision) = TP/(TP+FP):反映了被模型预测的正例中真正的正样本所占比重
召回率(Recall) = TP/(TP+FN):反映了被模型正确预测的正例占总的正样本比重
定位精度(IOU) = 两个框的交集大小/两个框的并集大小
融合结果 a)原始图像 b)红框显示传统图像处理识别的卡片 c)红框显示深度学习识别的卡片 d)红框显示融合b)和c)后的卡片
上图分别显示了不同方法识别的卡片,d)相对于b)的优势是提高了召回率,d)相对于c)的优势是提高了定位精度。
图一图二图三显示了一些其他实例图像的识别,每行图像是一类实例图,第一列是原始图像,第二列是传统图像处理识别的卡片,第三列是深度学习识别的卡片,第四列是融合的卡片。
图一图二能够准确识别卡片轮廓:
图一
图二
图三融合卡片的下边缘并没有完全贴合,这是因为融合步骤中修正dlbox1位置时,采用传统图像处理方法寻找临域范围内最近的直线,受到图像样式的影响,找到的直线并不是期望的卡片下边缘。
图三
实验过程中随机截取了50张闲鱼瀑布流卡片图像,共有卡片96个(不包含输入框),对每张图像分别采用传统图像处理方法、深度学习方法、融合方法得到卡片的识别结果,其中传统图像处理方法共识别出65个卡片,深度学习方法共识别出97个,融合后共识别出98个。精确率、召回率、定位精度如下表所示。融合后识别结果结合了传统图像处理方法定位精度高、深度学习方法召回率高的优点。
不同方法结果
前景算法小结
通过对闲鱼页面瀑布流卡片识别过程中的描述,我们简单介绍了前景处理的探索,通过机器视觉算法和机器学习算法协同完成前景元素的提取和识别。
结束语
本篇我们通过对前景提取和背景分析的介绍,提出了一种通过传统图像处理和深度学习相融合的方法,来得到高精确率、高召回率和高定位精度的识别结果。但方法本身还存在一些瑕疵,比如融合过程对组件元素进行修正时也会受到图像样式的干扰,后续这部分可以进一步进行优化。
原文链接
人工智能
2019-03-01 11:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
今天我来结合代码详细介绍一下如何用 SciSharp STACK 的 TensorFlow.NET 来训练一个线性回归的基本模型。线性回归模型是初入机器学习领域必修的基本模型。TensorFlow.NET为广大.NET开发者提供了一个除了ML.NET的第二个机器学习框架选择。
什么是线性回归?
线性回归是一种线性方法,利用数理统计中回归分析对因变量与一个或多个独立变量之间的关系进行建模,运用十分广泛。考虑单个因变量y和单个预测变量x的情况。 自变量也称为:协变量,输入,特征; 预测变量通常称为响应,输出,结果。 我们有一组数据并符合高斯分布,假设是一个简单的线性模型。 // Prepare training Data var train_X = np.array(3.3f, 4.4f, 5.5f, 6.71f, 6.93f, 4.168f, 9.779f, 6.182f, 7.59f, 2.167f, 7.042f, 10.791f, 5.313f, 7.997f, 5.654f, 9.27f, 3.1f); var train_Y = np.array(1.7f, 2.76f, 2.09f, 3.19f, 1.694f, 1.573f, 3.366f, 2.596f, 2.53f, 1.221f, 2.827f, 3.465f, 1.65f, 2.904f, 2.42f, 2.94f, 1.3f); var n_samples = train_X.shape[0];
基于给定的这组数据,我们要划一根线,使这根线最接近所有的红点。这根红线可以想成一个线性的等式:y = wx + b。我们要找一组最好的w和b的值,使他的损失函数值最小,理想情况下应该是零。
损失函数
损失函数帮助我们找出w和b的最佳值,这将为数据点提供最佳拟合线。由于我们找到这个w和b的最佳值,我们将此搜索问题转换为最小化问题,我们希望最小化预测值和实际值之间的误差。
我们选择以上等式来作为最小化的损失函数。 预测值和实际值之间的差异就是误差值。 我们对所有数据点的误差求和,并将该值除以数据点的总数。 这提供了所有数据点的平均平方误差。 因此,该成本函数也称为均方误差(MSE)函数。 现在,使用此MSE函数,我们将反复更新w和b的值,使MSE值在最小值处停止迭代。 // tf Graph Input var X = tf.placeholder(tf.float32); var Y = tf.placeholder(tf.float32); ​ // Set model weights var W = tf.Variable(rng.randn(), name: "weight"); var b = tf.Variable(rng.randn(), name: "bias"); ​ // Construct a linear model var pred = tf.add(tf.multiply(X, W), b); ​ // Mean squared error var cost = tf.reduce_sum(tf.pow(pred - Y, 2.0f)) / (2.0f * n_samples);
梯度下降
入门机器学习需要理解的另一个重要概念是梯度下降。 梯度下降是一种更新w和b的方法,以最小化成本函数。 我们的想法是,我们从w和b的一些随机初始值开始,然后我们迭代地更改这些值以找到最小损失值。 梯度下降有助于我们了解如何更新值或下一步的方向。 梯度下降也被称为最陡下降。
举一个类比,想象一下U形的坑,你站在坑的最高点,你的目标是到达坑的底部。 有一个条件,您只能采取离散的步骤到达底部。 如果您决定一步一步,最终会到达坑的底部,但这需要更长的时间。 如果你选择每次采取更长的步骤,你会很快达到,但是,你有可能超过坑的底部,而不是完全在底部。 在梯度下降算法中,您采取的步骤数是学习率。 这决定了算法收敛到最小值的速度。 // Gradient descent // Note, minimize() knows to modify W and b because Variable objects are trainable=True by default var optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost);
TensorFlow.NET实例了自动梯度下降的算法。完整代码请参考 Github 仓库。如果我们把这个线性模型计算图用TensorBoard展现出来,就是以下这个样子:
更详细的TensorFlow.NET使用文档请参考我的 在线帮助文档 。
人工智能
2019-03-01 11:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
美团作为全球最大的本地生活服务平台,拥有由遍布全国的市场人员所拍摄的众多门脸招牌图片数据。每张图片都是由全国的不同个人,采用不同设备,在不同地点,不同时间和不同环境下所拍摄的不同目标,是难得的可以公正评价算法鲁棒性和识别效果的图片数据,挑战也非常大。
近年来业界围绕着文字检测和文字识别提出了许多有效的算法和技术方案。由于之前公开的数据集普遍以英文为主,因此所提出的技术方案对中文特有问题关注不足。表现在以中文为主的实际应用场景中,这些技术方案的结果与应用预期差距较大。以美团掌握的某典型中文图片数据为例,在6000张图的图片数据集上(已去除文字无法辨识的图片),测试了国内最知名的三个AI开放平台,按字段统计识别率分别是94%,91% 和 86%,经过努力我们也只达到 98%。中文OCR在实际应用场景的表现并不乐观。
在此次 ICDAR2019上,我们挑选出很能代表中文特点的餐饮商家的门脸招牌图片来组织竞赛,这些招牌上的文字存在中文特有的设计和排版,同时也兼有自然场景文字识别中普遍存在的拍照角度、光照变化等干扰因素。我们希望通过竞赛引起同行们对中文识别的关注,群策群力解决中文识别的实际问题。
会议与大赛介绍
国际文档分析与识别国际会议 (International Conference on Document Analysis and Recognition,ICDAR)是由国际模式识别学会(IAPR)组织的专业会议之一,专注于文本领域的识别与应用。ICDAR大会每两年举办一次,目前已发展成文字识别领域的旗舰学术会议。为了提高自然场景的文本检测和识别水平,国际文档分析和识别会议(ICDAR)于2003年设立了鲁棒文本阅读竞赛(“Robust Reading Competitions”)。至今已有来自89个国家的3500多支队伍参与。
ICDAR 2019将于今年9月20-25日在澳大利亚悉尼举办。 美团今年联合国内外知名科研机构和学者,提出了"中文门脸招牌文字识别"比赛(ICDAR 2019 Robust Reading Challenge on Reading Chinese Text on Signboards)。
组织者 王栋、张睿、刘曦、周永生,美团 白翔、廖明辉、杨明锟,华中科技大学 Baoguang Shi, Microsoft (Redmond,USA) Shijian Lu, Nanyang Technological University (Singapore) Dimosthenis Karatzas,Computer Vision Centre,UAB(Spain) C. V. Jawahar,IIIT Hyderabad(India)
数据集介绍
美团本次公开的数据,由遍布全国的市场人员所拍摄的众多门脸招牌图片组成,共25000张。每张图片是由完全独立的不同个人,采用不同设备,在不同地点,不同时间和不同环境下所拍摄的不同商家。该数据集以中文文字为主,也包含一定数量的英文和数字,英文和数字的占比介于 10% 和 30% 之间。标注内容比较完备,每张图片均标注了单个字符的位置和文本,以及各字符串的位置和文本。是难得的用于研发和评估中文识别技术的数据集。其中,20000张图片用于训练,2000张用于验证,3000张用于测试。
比赛内容
本次门脸招牌识别,共定义了 4 个任务,分别如下: TASK 1:招牌端到端文字识别 TASK 2:招牌文字行定位 TASK 3:招牌区域内单字识别 TASK 4:招牌区域内字符串识别
奖励方式
颁发奖状:按照最终成绩进行排名,以从高至低顺序依次选取前三名,颁发奖状。 比赛奖金:从高至低顺序选取前三名参赛方为学校及科研院所等非盈利机构。 奖金详细如下:
重要日期 2019年3月1日:报名通道开放 2019年3月18日:训练数据集开放 2019年4月15日:测试数据集分批开放 2019年4月16日:提交通道开放 2019年4月30日:提交截止日期 2019年5月10日:比赛最终报告提交 2019年9月20日:ICDAR 2019 大会召开
参赛报名
扫描下方二维码,直接进入报名链接报名。
报名链接地址: http://rrc.cvc.uab.es/?ch=12 报名链接二维码:
参赛答疑与交流
参赛答疑邮箱:mtdptech@meituan.com(邮件标题请注明 “ICDAR2019”) 加入参赛交流微信群
步骤1:微信添加 “MTDPtech02” 为好友(昵称:美美),或扫描下方二维码直接添加:
步骤2:回复美美 “ICDAR2019”,则会自动将您加入ICDAR2019-ReCTS技术入群
人工智能
2019-03-01 11:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本文是csu_zipple 分享的关于使用hanlp汉语言处理包提取关键词的过程一个简单的记录分享。想要使用hanlp提取文本关键词的新手朋友们可以参考学习一下!
如何在一段文本之中提取出相应的关键词呢? 之前有想过用机器学习的方法来进行词法分析,但是在项目中测试时正确率不够。于是这时候便有了 HanLP-汉语言处理包 来进行提取关键词的想法。
下载:.jar .properties data等文件
请到大快搜索官网下载 HanLP新版本,1.7.1数据包下载【gitub上也可以下载】
在intellij中配置环境,并运行第一个demo
在项目中配置jar包,添加依赖。
file->Project Structure->Modules->Dependencies->+Jars
将properties文件转移到src根目录下,修改root为自己的数据集路径
运行第一个demo

1 public class TestHanLP {
2 public static void main(String[] args) {
3 System.out.println(HanLP.segment("你好,欢迎使用HanLP!"));
4 }
5 }

可能的错误

字符类型对应表加载失败:D:/BaiduYunDownload/data-for-1.3.3/data/dictionary/other/CharType.dat.yes

解决办法:查看错误提示页面下是否有该文件,如果没有则去网上下载一个。像我这里,由于只是使用其一部分功能,为了方便就不再下载了,这里我直接修改了一个文件的文件名—–成功运行!。
成功运行


人工智能
2019-03-01 08:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 本文由云+社区发表 作者:腾讯云学院
人人都能上手的机器学习免费实战课程!
帮助开发者搭建体系化的机器学习知识框架,熟悉主流机器学习算法,介绍利用机器学习平台完成实际场景下的模型搭建和模型评估的精选实战课程!
快扫码报名吧~
此文已由腾讯云+社区在各渠道发布
获取更多新鲜技术干货,可以关注我们 腾讯云技术社区-云加社区官方号及知乎机构号
人工智能
2019-02-28 18:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
源代码审查
概述
源代码审查是手动检查Web应用程序源代码以解决安全问题的过程。任何其他形式的分析或测试都无法检测到许多严重的安全漏洞。正如流行的说法“如果你想知道真正发生了什么,请直接找到源代码。”几乎所有安全专家都认为实际查看代码没有任何替代品。所有用于识别安全问题的信息都在代码在某处。与测试第三方封闭式软件(如操作系统)不同,在测试Web应用程序时(特别是如果它们是在内部开发的),源代码应该可用于测试目的。
许多无意但重要的安全问题也很难通过其他形式的分析或测试发现,例如渗透测试,使源代码分析成为技术测试的首选技术。使用源代码,测试人员可以准确地确定发生了什么(或者应该发生什么)并删除黑盒测试的猜测工作。
通过源代码审查特别有利于发现的问题示例包括并发问题,有缺陷的业务逻辑,访问控制问题,加密弱点以及后门,特洛伊木马,复活节彩蛋,定时炸弹,逻辑炸弹和其他形式的恶意码。这些问题通常表现为网站中最有害的漏洞。源代码分析也可以非常有效地查找实现问题,例如未执行输入验证的位置或可能存在失效打开控制过程的位置。但请记住,操作过程也需要进行审核,因为部署的源代码可能与此处分析的源代码不同[13]。
好处: 完整性和有效性 准确性 快速(适合有能力的评审员)
缺点: 需要高技能的安全开发人员 可能会错过编译库中的问题 无法轻松检测到运行时错误 实际部署的源代码可能与正在分析的源代码不同
渗透测试
概述
多年来,渗透测试一直是用于测试网络安全性的常用技术。它通常也被称为黑盒测试或道德黑客攻击。渗透测试本质上是远程测试正在运行的应用程序以查找安全漏洞的“艺术”,而不了解应用程序本身的内部工作原理。通常,渗透测试团队可以像访问用户一样访问应用程序。测试人员就像攻击者一样,试图查找和利用漏洞。在许多情况下,测试人员将获得系统上的有效帐户。
虽然渗透测试已被证明在网络安全性方面是有效的,但该技术并不能自然地转化为应用程序。在网络和操作系统上执行渗透测试时,大部分工作涉及查找并利用特定技术中的已知漏洞。由于Web应用程序几乎完全是定制的,因此Web应用程序领域的渗透测试更类似于纯粹的研究。已经开发了渗透测试工具来自动化该过程,但是由于Web应用程序的性质,它们的有效性通常很差。
今天许多人使用Web应用程序渗透测试作为其主要的安全测试技术。虽然它确实在测试程序中占有一席之地,但我们认为它不应被视为主要或唯一的测试技术。加里麦格劳[14]总结了渗透测试,他说,“如果你没有通过渗透测试,你知道你确实有一个非常糟糕的问题。如果你通过渗透测试,你不知道你没有一个非常糟糕的问题“。但是,有针对性的渗透测试(即尝试利用先前评论中检测到的已知漏洞的测试)可用于检测网站上部署的源代码中是否实际修复了某些特定漏洞。
好处: 可以快速(因此便宜) 需要比源代码审查相对较低的技能 测试实际暴露的代码
缺点: 在SDLC中太晚了 仅限正面碰撞测试。
平衡方法的必要性
由于有许多技术和方法来测试Web应用程序的安全性,因此很难理解使用哪种技术以及何时使用它们。经验表明,对于究竟应该使用哪种技术来构建测试框架的问题,没有正确或错误的答案。实际上,所有技术都应该用于测试所有需要测试的区域。
虽然很明显没有单一的技术可以有效地覆盖所有安全测试并确保所有问题都得到解决,但许多公司只采用了一种方法。历史上使用的方法是渗透测试。渗透测试虽然有用,但无法有效解决许多需要测试的问题。在软件开发生命周期(SDLC)中,它只是“太晚了”。
正确的方法是一种平衡的方法,包括从手动审查到技术测试的几种技术。平衡的方法应涵盖SDLC所有阶段的测试。该方法利用了最合适的技术,具体取决于当前的SDLC阶段。
当然,有时候和情况下只有一种技术是可能的。例如,对已创建的Web应用程序进行测试,但测试方无法访问源代码。在这种情况下,渗透测试显然比没有测试好。但是,应鼓励测试方质疑假设,例如无法访问源代码,并探索更完整测试的可能性。
平衡的方法取决于许多因素,例如测试过程的成熟度和企业文化。建议平衡测试框架应该类似于图3和图4中所示的表示。下图显示了覆盖软件开发生命周期的典型比例表示。为了与研究和经验保持一致,公司必须更加重视发展的早期阶段。

人工智能
2019-02-28 09:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
解决的目标问题:多分类问题,比如车辆的外形和颜色,苹果的大小和颜色;多任务:车牌角点的定位和车牌的颜色。定位在技术上属于回归,车牌颜色判断则属于分类。 技术点
caffe默认是单输入任务单标签的,也就是一个样本,其任务只有一个,标签只有一个,比如图片是什么颜色,图片是什么物体。 # ${caffe_src_root}/tools/convert_imageset.cpp 第121行 status = ReadImageToDatum(root_folder + lines[line_id].first, lines[line_id].second, resize_height, resize_width, is_color, enc, &datum); ## 其中 ReadImageToDatum的定义如下 ${caffe_src_root}/include/caffe/util/io.hpp bool ReadImageToDatum(const string& filename, const int label, const int height, const int width, const bool is_color, const std::string & encoding, Datum* datum); ## ${caffe_src_root}/src/caffe/util/io.cpp 中的该函数实现,涉及到Datum的定义,需要把Datum定义修改成也要支持多标签 bool ReadImageToDatum(const string& filename, const int label, const int height, const int width, const bool is_color, const std::string & encoding, Datum* datum) { cv::Mat cv_img = ReadImageToCVMat(filename, height, width, is_color); if (cv_img.data) { if (encoding.size()) { if ( (cv_img.channels() == 3) == is_color && !height && !width && matchExt(filename, encoding) ) return ReadFileToDatum(filename, label, datum); std::vector buf; cv::imencode("."+encoding, cv_img, buf); datum->set_data(std::string(reinterpret_cast(&buf[0]), buf.size())); datum->set_label(label); datum->set_encoded(true); return true; } CVMatToDatum(cv_img, datum); datum->set_label(label); return true; } else { return false; } }
为了支持多任务,多标签,首先要解决输入问题。比如一个样本 定义如下:
vehicle/1.jpg 0 1
修改源码支持多标签
其中第一个属性是车辆外形,0代表sedian,第二个属性是车身颜色,1代表白色。假如图片是60x60的RGB图像, 如果是单任务多属性输入,一个简单的更改方案是把ReadImageToDatum函数修改成如下定义,并修改相关的实现函数和convert_imageset.cpp bool ReadImageToDatum(const string& filename, const vector & labels, const int height, const int width, const bool is_color, const std::string & encoding, Datum* datum); faster rcnn采用自定义的python输入层作用训练输入,输入有多个labels,检测目标的roi,其中bbox_targets, bbox_inside_weights, bbox_outside_weights是作为SmoothL1Loss损失函数的输入。自定义python输入层的源码参考 py-faster-rcnn / lib / roi_data_layer / name: "VGG_ILSVRC_16_layers" layer { name: 'data' type: 'Python' top: 'data' top: 'rois' top: 'labels' top: 'bbox_targets' top: 'bbox_inside_weights' top: 'bbox_outside_weights' python_param { module: 'roi_data_layer.layer' layer: 'RoIDataLayer' param_str: "'num_classes': 21" } }
从https://github.com/HolidayXue/CodeSnap/blob/master/convert_multilabel.cpp源码修改,保存到${caffe_root}/tools/convert_multi_label_imageset.cpp,重新编译caffe工程,在${caffe_root}目录下运行该工具,
.build_release/tools/convert_multi_label_imageset.bin -resize_width=256 -resize_height=256 ~/my\ workspace/bounding-box-tool/mlds/train.list /train-data/vehicle-type-color-dataset/

多数据源输入支持多标签
假设对于HxW的RGB图像,转换成caffe的blob定义上1x3xHxW,对于一个任务的有n个标签,则其blob定义是1xnx1x1,每个任务对应一个blob, ???那么可以在在第二维度对两个blob进行拼接???
拼接之后再从第二维度对blob进行切分操作,切分出多个blob,作为每个属性训练任务的输入

拼接之后进行常规的卷积操作,只是在最后的每个任务的损失函数之前的fc层再切分,如下图
训练
参考faster-rcnn的模型,可以看到损失函数是相互独立的,但多了一个weight参数,猜测是caffe在训练时,按下面的公式计算总的损失
Lt = w1*L1 + w2 * L2
faster-rcnn中经过一系列卷积层后,连接了一个ROIPooling层,再接上FC6、FC7层,从最后一个FC7层一分为2,分别接一个cls_score的FC层和名为loss_cls的SoftMaxWithLoss,接bbox_pred的FC层和名为loss_bbox的SmoothL1Loss的回归层 参考:
https://arxiv.org/abs/1604.02878v1
https://kpzhang93.github.io/MTCNN_face_detection_alignment/index.html?from=timeline&isappinstalled=1
https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf
https://github.com/happynear/MTCNN_face_detection_alignment
https://github.com/naritapandhe/Gender-Age-Classification-CNN
https://github.com/cunjian/multitask_CNN
https://zhuanlan.zhihu.com/p/22190532
https://github.com/rbgirshick/py-faster-rcnn/blob/master/models/pascal_voc/VGG16/fast_rcnn/train.prototxt
${caffe_source_root}/examples/pascal-multilabel-with-datalayer.ipynb
http://www.cnblogs.com/yymn/articles/7741741.html
https://yq.aliyun.com/ziliao/572047
https://blog.csdn.net/u013010889/article/details/53098346
caffe网络在线可视化工具: http://ethereon.github.io/netscope/#/editor
人工智能
2019-02-28 09:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
概述
手动检查是人工审核,通常会测试人员,策略和流程的安全隐患。手动检查还可以包括检查技术决策,例如建筑设计。它们通常通过分析文档或与设计者或系统所有者进行访谈来进行。
虽然手动检查和人工审查的概念很简单,但它们可以是最有效和最有效的技术之一。通过询问某人如何工作以及为何以特定方式实施,测试人员可以快速确定是否可能出现任何安全问题。手动检查和审查是测试软件开发生命周期过程本身的几种方法之一,并确保有适当的策略或技能组合。
与生活中的许多事情一样,在进行人工检查和审查时,建议采用信任 - 验证模型。并非测试仪显示或告知的所有内容都是准确的。手动审核特别适用于测试人们是否了解安全流程,了解策略以及是否具备设计或实施安全应用程序的适当技能。
其他活动,包括手动审阅文档,安全编码策略,安全要求和架构设计,都应使用人工检查完成。
好处: 不需要支持技术 可以应用于各种情况 灵活 促进团队合作 早在SDLC
缺点: 可能很费时间 支持材料并非始终可用 需要重要的人类思想和技能才能有效
威胁建模
概述
威胁建模已成为一种流行的技术,可帮助系统设计人员考虑其系统和应用程序可能面临的安全威胁。因此,威胁建模可视为应用程序的风险评估。实际上,它使设计人员能够针对潜在漏洞制定缓解策略,并帮助他们将不可避免的有限资源和注意力集中在最需要它的系统部分上。建议所有应用程序都开发并记录威胁模型。威胁模型应尽早在SDLC中创建,并应随着应用程序的发展和开发的进展而重新审视。
为了开发威胁模型,我们建议采用遵循NIST 800-30 [11]风险评估标准的简单方法。这种方法涉及: 分解应用程序 - 使用手动检查过程来了解应用程序的工作方式,资产,功能和连接性。 定义和分类资产 - 将资产分类为有形和无形资产,并根据业务重要性对其进行排名。 探索潜在的漏洞 - 无论是技术,运营还是管理。 探索潜在威胁 - 通过使用威胁场景或攻击树,从攻击者的角度开发潜在攻击媒介的真实视图。 制定缓解策略 - 针对每种被认为是现实的威胁制定缓解控制措施。
威胁模型本身的输出可能会有所不同,但通常是列表和图表的集合。OWASP代码审查指南概述了应用程序威胁建模方法,该方法可用作测试应用程序的参考,以应对应用程序设计中的潜在安全漏洞。没有正确或错误的方法来开发威胁模型并对应用程序进行信息风险评估。[12]。
好处: 实用的攻击者对系统的看法 灵活 早在SDLC
缺点: 相对较新的技术 好的威胁模型不会自动意味着良好的软件

人工智能
2019-02-28 09:11:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以前就自己学会了转一面,后来花了很长时间自己研究出转2面
最近仔要转6面,就看网上介绍知道了层先法
自己摸索着解决了第一层,倒T形
然后是第二层,网上公式不少,但经常对比,比较顺手的是RU大法,因为不用转前面。初学转前方的话很容易忘记正面所在。
然后是顶黄十字,可以用FRU大法,很快搞定
然后是全黄的解法,网上公式也很多,而且要对应不同的情况,先是归纳了R'UURUR'U的用法。 3个空,左下角为顺时针的开始。 2个空:相反方向朝前,其他朝右
这个公式也比较强,但有时要转4次才能全黄,比较心累
然后是顶角归位,网上的公式简直无法记住,一点规律都没有,还需要转后方!大脑经常要停顿思考B'是怎么转的
终于第二天看了魔方小站的视频,发现一个神奇的手法,都不用记公式,看了二次就记得了: 白1,反L,下1、左二、恢复反L,恢复白1,下1,左二回,下1回
最后一步,就是RU'RU RU RU'大法。
由于全黄的解法比较心累,步数太多,于是第三天又看视频研究解法,知道了小鱼公式的转法。小鱼本身看公式非常晕,但如果记手法的话就很容易。但小站上的小鱼手法不够好,于是去看小鱼公式,经过一晚上的分析,结合之前的反L手法,得出了以下公式: 四空:找1或0面黄朝前,正常转法 三空:靠近鱼头的黄朝前,一次全黄 二空:相反方向或对角的话,用反方向小鱼。同向的则无所谓
这个公式厉害之处在于2次就一定能够完成全黄。而网上的公式不知是故意的,还是真的作者太菜,居然还有需要4次的小鱼!
小鱼本身也能用于最后一步,但需要左、右小鱼各一次,还是很麻烦的,远不如RU'的解法无脑,而且RU'解法有时能够一次完成
人工智能
2019-02-27 22:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
闲鱼技术-上叶
背景:
  随着移动互联网时代的到来,人类的科学技术突飞猛进。然而软件工程师们依旧需要花费大量精力在重复的还原UI视觉稿的工作。
UI视觉研发拥有明显的特征:组件,位置和布局,符合机器学习处理范畴。能否通过机器视觉和深度学习等手段自动生成UI界面代码,来解放重复劳动力,成为我们关注的方向。
UI2CODE简单介绍:
  UI2CODE项目是闲鱼技术团队研发的一款通过机器视觉理解+AI人工智能将UI视觉图片转化为端侧代码的工具。
  2018年3月UI2CODE开始启动技术可行性预研工作,到目前为止,经历了3次整体方案的重构(或者重写)。我们参考了大量的利用机器学习生成代码的方案,但都无法达到商用指标,UI2CODE的主要思想是将UI研发特征分而治之,避免鸡蛋放在一个篮子里。我们着重关注以下3个问题的解法: 视觉稿还原精度:我们的设计师甚至无法容忍1像素的位置偏差; 准确率:机器学习还处于概率学范畴,但我们需要100%的准确率; 易维护性:工程师们看的懂,改的动是起点,合理的布局结构才能保障界面流畅运行。
UI2CODE运行效果:
  UI2CODE插件化运行效果,如下视频:进过几轮重构,最终我们定义UI2CODE主要解决feeds流的卡片自动生成,当然它也可以对页面级自动生成。
视频链接:https://yunqivedio.alicdn.com/od/mm4Te1551157852340.mov
架构设计:
简化分析下UI2CODE的流程:

大体分为4个步骤: 1.通过机器视觉技术,从视觉稿提取GUI元素 2.通过深度学习技术,识别GUI元素类型 3.通过递归神经网络技术,生成DSL 4.通过语法树模板匹配,生成flutter代码
版面分析
  版面分析只做一件事:切图。
  图片切割效果直接决定UI2CODE输出结果的准确率。我们拿白色背景的简单UI来举例:

上图是一个白色背景的UI,我们将图片读入内存,进行二值化处理: def image_to_matrix(filename): im = Image.open(filename) width, height = im.size im = im.convert("L") matrix = np.asarray(im) return matrix, width, height
得到一个二维矩阵:将白色背景的值转化为0.
像切西瓜一样,我们只需要5刀,就可以将GUI元素分离,切隔方法多种多样:(下面是横切的代码片段,实际切割逻辑稍微复杂些,基本是递归过程) def cut_by_col(cut_num, _im_mask): zero_start = None zero_end = None end_range = len(_im_mask) for x in range(0, end_range): im = _im_mask[x] if len(np.where(im==0)[0]) == len(im): if zero_start == None: zero_start = x elif zero_start != None and zero_end == None: zero_end = x if zero_start != None and zero_end != None: start = zero_start if start > 0: cut_num.append(start) zero_start = None zero_end = None if x == end_range-1 and zero_start != None and zero_end == None and zero_start > 0: zero_end = x start = zero_start if start > 0: cut_num.append(start) zero_start = None zero_end = None
客户端的UI基本都是纵向流式布局,我们可以先横切在纵切。
将切割点的x,y轴坐标记录下来,它将是处理组件位置关系的核心。切割完成后,我们获取到2组数据:6个GUI元素图片和对应的坐标系记录。后续步骤通过分类神经网络进行组件识别。
在实际生产过程中,版面分析会复杂些,主要是在处理复杂背景方面。

关注我们的技术公众号,我们后续会详细分解。
组件识别:
  进行组件识别前我们需要收集一些组件样本进行训练,使用Tensorflow提供的CNN模型和SSD模型等进行增量训练。
  UI2CODE对GUI进行了几十种类型分类:IMAGE, TEXT,SHAP/BUTTON,ICON,PRICE等等,分别归类为UI组件,CI组件和BI组件。 UI组件,主要针对flutter原生的组件进行分类。 CI组件,主要针对闲鱼自定义UIKIT进行分类。 BI组件,主要针对具备一定业务意义的feed卡片进行分类。

组件的识别需要反复的通过全局特征反馈来纠正,通常会采用SSD+CNN协同工作,比如下图的红色“全新“shape,这该图例中是richtext的部分,同样的shape样式可能属于button或者icon。
属性提取:
  这块的技术点比较杂,归纳起来需要处理3部分内容:shape轮廓, 字体属性和组件的宽高。

完成属性提取,基本上我们完成所有GUI信息的提取。生成的GUI DSL如下图:

通过这些数据我们就可以进行布局分析了。
其中文字属性的提取最为复杂,后续我们会专门介绍。
布局分析:
  前期我们采用4层LSTM网络进行训练学习,由于样本量比较小,我们改为规则实现。规则实现也比较简单,我们在第一步切图时5刀切割的顺序就是row和col。缺点是布局比较死板,需要结合RNN进行前置反馈。
视频连接:https://yunqivedio.alicdn.com/od/7DD7w1551157938106.mp4
视频中展示的是通过4层LSTM预测布局结构的效果,UI的布局结构就像房屋的框架,建造完成后通过GUI的属性进行精装修就完成了一个UI图层的代码还原工作。
代码生成及插件化:
  机器学习本质上来说还属于概率学范畴,自动生成的代码需要非常高的还原度和100%的准确率,概率注定UI2CODE很难达到100%的准确率,所以我们需要提供一个可编辑工具,由开发者通过工具能够快速理解UI的布局结构和修改布局结构。
  我们将UI2CODE生成的DSL TREE进行代码模板化匹配,代码模板的内容由资深的flutter技术专家来定义,它代表目前我们发现的最优代码实现方案。
代码模板中会引入一些标签,由Intellij plugin来检索flutter工程中是否存在对应的自定义UIKIT,并进行替换,提高代码的复用度.
整个插件化工程需要提供自定义UIKIT的检索,替换和校验工作,以及DSL Tree的创建,修改,图示等工作,总体来说,更像ERP系统,花费一些时间能够做的更加完美。
小结:
  本篇我们简单介绍了UI2CODE的设计思路,我们将整个工程结构分为5个部分,其中4块内容核心处理机器视觉的问题,通过机器学习将它们链接起来。代码的线上发布是非常严格的事情,而单纯的机器学习方式,很难达到我们要求,所以我们选择以机器视觉理解为主,机器学习为辅的方式,构建整个UI2CODE工程体系。我们将持续关注AI技术,来打造一个完美的UI2CODE工具。
原文链接
人工智能
2019-02-27 13:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 1排序算法分类 冒泡排序 选择排序 插入排序 希尔排序 归并排序 快速排序 推排序 计数排序 桶排序 基数排序 2 计算代码如下
  冒泡排序 function bubbleSort(arr) { let len = arr.length; for(let i = 0;jarr[j+1]){ [arr[j+1],arr[j]] = [arr[j],arr[j+1]]; } } } return arr; } //快排demo: function quickSort(arr,left,right) { let len = arr.length; let partitionIndex; left = typeof left != 'number'? 0 :left; right = typeof right !='number'? len -1 :right; if(left=0 && arr[preIndex]>current) { arr[preIndex+1] = arr[preIndex]; preIndex--; } arr[preIndex+1] = current; } return arr; } // 归并排序:Mozilla Firefox 使用归并排序作为Array.prototype.sort的实现,而chrome使用快速排序的一个变体实现的,前面三种算法性能不好,但归并排序性能不错 算法复杂度O(nlog^n) function mergeSort (arr) { let len = arr.length; if(len<2) { return arr } let middle = Math.floor(len/2), left = arr.slice(0,middle); return merge(mergeSort(left),mergeSort(right)); } function merge(left, right){ let result = []; while (left.length && right.length) { if (left[0] <= right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } result.push(...left); result.push(...right); return result; //堆排序:堆排序把数组当中二叉树来排序而得名。 var len; function buildMaxHeap(arr) { len = arr.length; for(let i = Math.floor(len/2;i>=0;i--){ heapify(arr,i) } } function heapify(arr,i) { let left = 2* i+1; let right = 2*i+2; let largest = i; if(left arr[largest]) { largest = left; } if(right < len && arr[right]>arr[largest]){ largest = right; } if (largest !== i) { [arr[i], arr[largest]] = [arr[largest], arr[i]]; heapify(arr, largest); } } function heapSort(arr) { buildMaxHeap(arr); for (let i = arr.length - 1; i > 0; i--) { [arr[0],arr[i]]=[arr[i],arr[0]]; len--; heapify(arr, 0); } return arr; }
人工智能
2019-02-27 10:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
机器学习算法发展比较迅速,但为了能够实现工程化的规模化应用,还需要稳定的框架和一致的流程的支持。
目前的机器学习算法主要分为两类,即深度学习与传统的机器学习。传统的机器学习如随机森林、主成分分析等主要是基于统计、概率的单层算法,一般都有复杂的计算公式;而深度学习是基于多层神经网络的计算模式,响应函数往往并不是很复杂,但多个层之间的传导函数和学习(概率统计)后的参数的数据量比较大,训练过程中使用多个参数进行试验的计算量也非常大,往往需要GPU或专用芯片才能更快地完成;根据应用数据和模式的不同,深度学习已经衍生出了大量的具体算法。
传统的机器学习算法已经有大量成熟的库可以直接调用,在Spark中也可以通过MLlib进行机器学习模型参数的解算和应用。这里,主要探讨深度学习相关的技术和框架的发展及其未来可能的趋势。
1、深度学习技术栈
主要包括: 芯片与驱动软件 ,提供传统CPU/GPU以及新型神经网络专用计算芯片和驱动软件。 存储与计算设施 ,将大数据存储、处理系统与深度学习相结合,并在云计算中心提供服务。 机器学习计算引擎 ,提供高性能神经网络解算引擎(概率统计和线性代数的快速迭代计算)和DAG(计算图)。 基础库管理 ,包括训练样本库、应用数据库、模型参数库、模型代码库的存储、管理和处理、输入输出工具。 运行框架与流程 ,提供运行环境、图形化UI、标准流程、统一接口、语言与模型,支持集群和云计算环境。
2、芯片与驱动软件
芯片与驱动软件,提供传统CPU/GPU以及新型神经网络专用计算芯片和驱动软件。目前训练过程以CPU和GPU计算为主,移动端应用以专用嵌入式芯片为主。 Nvidia的GPU计算体系仍然是机器学习训练的主流平台,为了加速训练过程,Nvidia推出了DGX/HGX系列一体机,其中HGX-2 由 16 块 NVIDIA® Tesla® V100 GPU 和 NVIDIA NVSwitch™ 高速计算平台。提供了相应的软件支持包(CUDA/Cognitive/Caffe2等,参阅 https://developer.nvidia.com/deep-learning-frameworks ),以及容器的GPU支持引擎(Nvidia-docker),为容器和Kubernetes集群环境提供机器学习支持。 Kubernetes安装GPU支持插件 Kubernetes中调度GPU资源 Kubernetes集群升级NVidia GPU驱动版本 Intel的Xeon芯片提高了更为强大的多核计算能力,而且在服务器上可以组成多路,以及通过多节点集群来进行并行化计算。对于计算负载不是特别大的任务可以直接使用CPU完成。Intel同时也通过收购其它公司在服务器(Nervana™神经网络处理器)和移动端(Movidius™ 神经计算模块)的深度学习芯片上发力,试图将CPU和FPGA的计算能力实现整合,目前应用还在开发之中。 专用的神经网络芯片发展迅速,主要有FPGA(现场可编程门阵列)、DSP(数字信号处理)、ASIC(专用集成电路)、ARM扩展模块等技术路线,具有速度快、带宽高、功耗低、体积小的优势,主要面向移动和嵌入式系统。muqian 主要的厂家有苹果、高通、华为、商汤科技、地平线、中芯、Face++、比特大陆等,而且很多厂家在芯片里已经固化了图像处理、目标识别等基础模型和算法,可以快速集成到手机或其他设备之中,目前主要功能以人脸识别、照片分类、图像处理、图像风格迁移、图像超分辨率重建、车牌识别、智能安防、自动驾驶、无人机姿态保持与位置追踪等领域为主。
3、 存储与计算设施
深度学习的训练和应用往往涉及到大量的数据存储和处理,与大数据技术的存储和计算体系有密切的联系。目前新型的Linux基础存储管理软件如ZFS/Btrfs能提供更好的数据IO和磁盘管理能力,开源的HDFS/GlusterFS/Ceph等提供分布式的集群存储,Hadoop/Spark等提供集群化的计算能力,都能够更好地满足企业和科研机构自行搭建深度学习平台的需要。
大型云计算厂商(如Amazon、Azure、Google、Aliyun、Huawei以及Databrick、Nvidia)等都提供了基于云计算模式的深度学习基础设施服务,包括虚拟主机(预装了深度学习软件,支持GPU加速)、核心引擎和部分算法以及实验数据。可以购买相应的服务器实例即可快速启动自己的机器学习服务,而且可以通过云计算中心来扩展自己的节点从而缩短模型训练的总体时间。不过对于国内来说,目前租用机器学习服务器实例的成本还是比较高的,而且带宽较低会影响大量数据上传、下载的效率。
4、深度学习计算引擎
深度学习引擎包括基础的概率统计、线性代数的计算模块,以及根据具体应用开发的算法模型和模型参数库。
4.1 基础计算引擎
目前有多种广泛采用的深度学习神经网络计算引擎,包括: Tensorflow ,提供基于图(Graph)的张量计算引擎,2019.03.06发布了2.0版本。支持服务器、工作站和嵌入式设备, 目前有大量的外围库和领域模型可供使用。 MXNet ,目前是1.4.0版本,由Apache基金会支持,目前还处于孵化阶段。MXNet的高层接口是Gluon,Gluon同时支持灵活的动态图和高效的静态图,既保留动态图的易用性,也具有静态图的高性能。Amzon的机器学习云实例主要采用MXNet。 PyTorch / Caffe2 ,主要是FaceBook支持,也是广泛使用的深度学习库,在图像处理(分割、重建、识别等)方面有大量的应用模型。 SciKit / NumPy ,是在Python科学计算中广泛使用的数学库,加入了更多的神经网络算法,可以用于机器学习和深度学习。 ......
4.2 应用算法模型
深度学习的应用模块包括扩展模块、领域模型两种主要类型。扩展模块与核心引擎高度相关,提供数据的预处理、输出/输出、格式交换等功能。领域模型以文字处理、语音识别、图像处理、目标识别等为主,已经发展出了多种应用模型和软件、设备,如智能摄像头、智能停车收费、同声翻译机、智能音箱、图像风格迁移、照片换脸、视频套用、机器作曲、机器绘画、自动驾驶等等应用,也在医疗、安防等专业领域做出了有益的尝试。其中,一些开源的应用算法模块和系统包括: 测试框架, OpenAI Charter 由 OpenAI 发起的开源项目。 交换格式,ONNX试图在多种计算引擎间共享算法模型,从而简化应用端的工作。 图像分割 ,实现图形的区域分割,是图像精确分类、目标识别、自动驾驶等应用的基础算法,已经有大量的针对不同应用的算法模型。 图像分割综述【深度学习方法】, https://blog.csdn.net/weixin_41923961/article/details/80946586 深度对抗学习在图像分割和超分辨率中的应用, https://zhuanlan.zhihu.com/p/25201511 Facebook开源了三款人工智能图像分割(Image Segmentation)软件,分别是 DeepMask 、 SharpMask 和 MultiPathNet ,三款工具相互配合完成一个完整的图像识别分割处理流程,DeepMask 生成初始对象 mask、SharpMask 优化这些 mask,最后由 MultiPathNet 来识别这些 mask 框定的物体。 风格迁移,将图片从一种风格转换为另一种风格,如将黑白照片上为彩色,将普通照片变为优化等等。著名的 DayDream 项目可以将多个照片合成为虚幻的画作。 文字处理,通过神经网络来实现分词、搜索、合成,以及文章创作等高级功能。 语音识别,将语音转换为文字,是自动翻译的前置步骤。目前成熟度比较高,DeepMind/Amazon/讯飞/小米/华为等都研究该领域的技术,实现语音控制App、智能音箱、即时翻译等产品。 语音合成,将文字转为语音,与传统TTS不同的是,不仅可以将文字转为声音,还能匹配语音的风格,以及从现有语音提取风格并予以模拟,已经可以达到乱真的程度,成功通过盲测。 图像合成,将不同图像的部分合成为新的图像,如著名的换脸程序-Deepfake可以将视频中的头像与其它视频的部分合成为一体,造出虚假的视频,如果与语音仿真结合起来,可以制造出很难识别的虚假图像和视频。 图像修复,用于图像修复,如填充、抠图、去除图像污渍、消除多余目标等。 图像超分辨率重建,从低分辨率图像生成高精度图像,提升显示效果。NVidia使用实时超分辨率技术提高游戏画面的精细程度,效果非常惊人。 人脸识别 ,主要用于身份验证(支持手机解锁、手机支付、入场检测等等),已经比较成熟,开始逐步取代指纹验证方式。 车牌识别,用于自动化的停车收费系统、车辆出入控制、违章驾驶监测等等,已经比较成熟,开始广泛使用。 目标识别,从比较图像中检测出目标图像,并进行分类识别。包括对伪装目标的检测,自动化制图,变化检测等等应用方向。 医疗图像,对疾病图像进行深度学习后,根据模型来自动判读各种医疗图像,识别异常部位和病理目标,实现自动诊断疾病。 地球科学,进行空间图谱分析、全球变化监测,用于土地利用分类、土地用途管制的遥感影像自动检测,水体、大气污染与模式识别,灾害天气识别与预警等等。 机器视觉,用于识别周围环境,可以用于自动化设备、机器人、损伤检测等工作。大疆无人机用其实现避障、姿态控制等功能。 自动驾驶,通过对全向雷达和360图像的智能处理,对车道和周边环境实时感知、重建、识别,计算出最佳驾驶行为并控制车辆。
5、 基础库管理
机器学习平台会使用到大量的数据库,包括训练样本库、应用数据库、模型参数库、模型代码库的存储、管理和处理、输入输出工具,可以通过大数据平台或云存储、网络存储等平台进行管理和快速提取。 训练样本库。收集的标准实验数据,已经进行标记,用于在“机器学习”过程将特征值与标记值建立函数关系。 在神经网络中,该函数关系使用函数和参数集合来表达,使用多层网络来构建该超参数集。 往往需要对模型参数、层数进行多次实验,计算出误差值,然后进行逐步逼近最佳参数。 应用数据库。需要求算最终标记值的原始数据集,如人脸识别中新采集的图像。将输入“学习到”的神经网络函数和参数表达式,进行目标的判别解算。 模型参数库。除函数之外,深度学习需要大量参数来控制网络函数的传导。这些参数与调参代码、基础函数共同构成完整的模型。 模型代码库。可以使用Gitlab等版本管理系统进行管理。
6、深度学习框架与流程
除了TensorFlow之类的核心计算架构外,深度学习还需要与存储、计算、网络、客户端等计算基础设施打交道,就需要构建一个运行框架(包括服务、接口、应用),并形成一个标准化的流程,实现基础数据的管理和动态数据的学习和应用,并进一步实现反馈,实现自我优化学习,不断精炼模型参数。 运行环境。现在的趋势是将机器学习的训练环境放入容器中运行,然后通过Kubernetes来实现实例调度和容错、扩容等能力。从而可以在多种环境包括云计算中心间进行迁移和快速扩容,满足大规模的训练和应用的“机器智能”计算服务需求。 调度框架。Kubernetes提供了实例的调度、虚拟网络、虚拟存储和服务聚合机制,但是实例之间需要进行实时通讯以共享深度学习的参数库,以便进行模型的整体优化,这需要专门开发相应的机制来实现。 TensorFlow通过gRPC调用来实现多个节点的通讯和集群化数据同步,内置支持但与其它的服务架构(如REST/Kubernetes)融合管理有点复杂。 Spark的RDD可以在多个节点间高速同步数据(共享内存数据结构),也是可以用作超参数管理的(目前还没有用到),是理想的机制。 共享数据库可以通过传统的数据库来保存和共享超参数,可以支持事物保持一致性,但高频次查询的性能有一定影响。 共享存储可以通过HDFS/Ceph/Gluster或其它网络存储服务等保存和共享超参数,容量大但同步机制还需要自己加锁。 工作流程。深度学习需要经过数据导入、整理、归一化、标记等预处理过程,实验、评估、调参等训练过程,模型导出、环境迁移、软件开发、集成整合、测试验证等应用过程。流程的每一环节都需要相应的软件工具和工作方法论支持,才能顺利完成。
深度学习框架需要考虑到基础环境、数据同步机制和工作流程,从而提供一个单一的软件工具给开发者使用,简化目前的散乱的机器学习研究方法和基础设施,形成可以标准化交付的软件和应用模型,实现工程化应用。正在开发的框架包括: Kubeflow ,专为Kubernetes而设计的深度学习系统。采用TesnorFlow作为主要的计算内核(可以支持其它计算引擎,开发中),使用ksonnet作为应用部署工具(这与Helm安装不太一样),可以支持Argo CD作为DevOps工具,集成了JupuyterHub的Notebook交互服务,可以批量执行训练任务。Kubeflow目前版本0.4.1,还在快速迭代之中。 MLflow ,由Spark的主要开发者DataBricks提出并开发,可以运行于任何环境的python库,实现模型试验、结果比较与可视化、模型打包输出、重复使用的完整流程,目前版本为0.8.2。可以运行于基本python环境以及标准的JupyterHub环境中,适应性非常好,使用比较简单,能够整合到整个python的生态环境之中,从而可以利用大量的python模块和几乎所有的机器学习模型。 AirFlow ,是基于DAG(有向无环图)的通用的可编程计算工作流支持软件,正在开发Kubernetes的Operator,提供对容器集群的支持。深度学习的算法可以通过工作节点整合到整个流程之中,从而提供完整的机器学习一体化流程。目前版本是1.10.2,尚未提供对深度学习计算的针对性支持(如模型管理、训练任务、参数调优、模型输出等),但可以通过其它python库和容器来完成。 Nauta ,是英特尔针对Kubernetes环境开发的深度学习开源工具,目前主要为Intel Xeon而优化,还处于早期开发阶段。Nauta集成了JupyterHub,采用TensorFlow作为计算引擎,Kubernetes和Docker作为运行环境。
上面的框架虽然支持分布式环境,可以在多个节点间分派任务,但是多个节点尚未实现同步训练,有待进一步开发。除此之外,mxnet/pytorch/aws/azure/IBM/aliyun/huawei等也都在试图发展相应的深度学习框架和流程支持软件,部分已经进入试运行状态。
更多机器学习框架参考 基于Kubernetes的机器学习系统 Ubuntu快速玩转机器学习 Airflow在Kubernetes上的操作器 Deep Learning Models on Kubernetes with GPUs Spark的GPU支持方法研究 机器学习技术研究报告-2016年10月 Land Cover Classification Using the Geo AI 深度学习的基础设施(英) The Google Brain Team — Looking Back on 2017 (Part 1 of 2) Adaptable DL with nGraph™ Compiler and ONNX* NVidia Kubernetes中调度GPU资源 Kubernetes集群升级NVidia GPU驱动版本 Kubernetes安装GPU支持插件 Ubuntu 安装NVidia驱动 Ubuntu18.04上安装RTX 2080Ti显卡驱动 TensorFlow 快速运行TensorFlow的6种方式 Kubernetes集成TensorFlow服务 PyTorch PyTorch支持Kubernetes集群 MLFlow Spark机器学习工具链-MLflow简介 Spark机器学习工具链-MLflow使用教程 为JupyterHub自定义Notebook Images Databook-数据之书 KubeFlow Kubeflow 使用指南 Kubeflow更新升级到0.4.1 Kubeflow等镜像部署到集群多节点 Kubeflow镜像的快速下载(V0.3.3) Kubeflow 快速入门 Nauta 英特尔分布式深度学习平台Nauta-使用指南 英特尔分布式深度学习平台Nauta-安装、配置与管理指南
人工智能
2019-03-03 10:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
在Java或者C++中,我们把表达式和语句看做两种不同的东西。表达式有值,而语句执行动作。
在Scala中,几乎所有构造出来的语法结构都是有值的。这个特性使得程序更加的精简,也更易读。
1、条件表达式 scala> val x = 1 x: Int = 1 scala> val res = if(x == 1) 1 else 0 res: Int = 1 scala> var res = if(x == 1) "hello" else 3 res: Any = hello
2、语句终止
Scala的语句无需添加类似Java和C++的分号 ; 表示结尾,编译器会自动判断。当然,如果单行下存在多个语句,那么则需要用分号隔开前面的n-1个语句: if ( x > 1) { r = r * n; n = n -1 }
3、块表达式和赋值
1、 块表达式
{ } 表示一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。 val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx *dx + dy * dy)}
可以看到,这样的语法可以很干净的让dx、dy等对外部不可见了。
2、 赋值语句
一个以赋值语句结束的块,返回的是Unit类型的值。因此,类似于这样的操作可能和java中的不一样 x = y = 1
显然 x 的值为 y = 1 ,即(),也是Unit类型。前面我们提到过一次性初始化的方式 scala> val x, y = 1; x: Int = 1 y: Int = 1
4、输入和输出
1、 普通输出 scala> print("I love you.") I love you. scala> println("I love you too.") I love you too. 换行
2、 字符串插值输出 scala> val name = "akuya" name: String = akuya scala> print(f"I love $name!") I love akuya!
格式化的字符串是Scala类库定义的三个字符串插值器之一。通过不同的前缀采取不同的输出策略: s: 字符串可以包含表达式但不能有格式化指令; row:转义序列不会被求值。例如 raw"\n love." 其中的 \n 会原样输出 f:带有C风格的格式化字符串的表达式。
3、 控制台读取
可以使用scala.io.StdIn的readLine方法从控制台读取一行输入。
5、循环
1、 while
和java的使用一致
2、 for
和java的使用有所区别,其使用方式如下所示 for(i <- 表达式)
例如 scala> for(i <- 1 to 10) println(i) 1 2 3 ...
可以看到中间的特殊符号 <- 表示让变量i遍历(<-)右边的表达式的所有值。至于这个遍历具体的执行方式,则取决于后面的表达式的类型。对于集合而言,他会让i依次取得区间中的每个值。例如: scala> for(i <- "abcde") print(s" $i") a b c d e
6、高级for循环
生成器 : >- 后面的表达式。
守卫 :每个生成器都可以带上守卫,一个以if开头的Boolean表达式 scala> for(i <- 1 to 3; j <- 1 to 3 if(i != j)) print(f"(i=$i,j=$j) ") (i=1,j=2) (i=1,j=3) (i=2,j=1) (i=2,j=3) (i=3,j=1) (i=3,j=2) 注意在if之前没有分号,多个生成器之间需要分号
定义 :在循环中对变量赋值,从而引入变量。 scala> for(i <- 1 to 3; j <- 1 to 3;home = i) print(f"(i=$i,j=$j,home=$home) ") (i=1,j=1,home=1) (i=1,j=2,home=1) (i=1,j=3,home=1) (i=2,j=1,home=2) (i=2,j=2,home=2) (i=2,j=3,home=2) (i=3,j=1,home=3) (i=3,j=2,home=3) (i=3,j=3,home=3)
其中 home = i 就是一个定义。
yield :如果for循环的循环体以yield关键字开始,则该循环会构造出一个集合,每次迭代生成集合中的一个值: scala> val vec = for (i <- 1 to 20) yield i % 2 vec: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0)
7、函数
注意与类的方法进行区分。
在Java中函数只能用类的静态方法来模拟。
定义函数 def abs(x:Double) = if(x >= 0) x else -x
如果函数不是递归的,就不需要指定返回值类型。
同样,块中的最后一个表达式的值就是函数的值。
如果是递归函数,则需要定义返回值类型 def fac(n: Int): Int = {...}
8、默认参数和带名参数
调用某些函数时可以不必显式的给出所有参数值,对于这些函数我们可以使用默认参数。 def decorate(str: String, left: String = "[", right: String = "]"){...} // 调用 decorate("hello") // 调用2 decorate("hello","(",")")
9、变长参数 def sum(args: Int*) = {...}
10、过程
Scala对于不返回值的函数有特殊的表示法。如果函数体包含在花括号中但没有前面的 = ,那么返回类型就是Unit。这样的函数被称为 过程 。 // 省略了=号 def box(s: String) {...}
当然,既然返回值是Unit类型,那么过程也可以用如下的方法定义 def box(s: String): Unit = {...}
11、懒值
当val被生命为lazy时,他的初始化将会被推迟,直到我们首次对他取值。很像Linq或者Spark RDD等许多数据处理框架的 惰性原理 。 scala> lazy val words = scala.io.Source.fromFile("/noexist.file") words: scala.io.BufferedSource = scala> words.toString java.io.FileNotFoundException: \noexist.file (系统找不到指定的文件。)
可以看到,即便我们一开始的words取的是一个不存在的文件,也没有立即报错,而是在我们对words进行取值之后才出现了错误。
可以把懒值理解为val和def的中间状态。
12、异常
Scala和java不一样,不支持“受检异常”。
throws表达式有特殊的类型Nothing。
如果一个分支的类型是Nothing,那么 if/else 表达式的类型就是另一个分支的类型。
捕获异常的语法采用的是模式匹配语法。
L、练习
1、一个数字如果为正数,则他的signum为1;如果是负数,则signum为-1,;如果是0,则signum为0.编写一个函数来计算这个值。 package com.zhaoyi.c2 object Practice2 { def signum(x: Int): Int = { if(x > 0){ 1 } else if( x == 0){ 0 }else{ -1 } } def main(args: Array[String]): Unit = { println("signum(100) = " + signum(100)); println("signum(0) = " + signum(0)); println("signum(-100) = " + signum(-100)); // 或者使用系统函数 println("use system signum(10) = " + BigInt(10).signum); } }
输出结果: signum(100) = 1 signum(0) = 0 signum(-100) = -1 use system signum(10) = 1
2、一个空的块表达式 {} 的值是什么?类型是什么? scala> var s = {} s: Unit = ()
Unit表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
3、指出在scala中何种情况下赋值语句 x = y = 1 是合法的。
显然,只需要有“需要x的值为()”的时候,这样的需求是合法的。
4、针对下列Java循环编写一个Scala版的程序。 for(int i = 10;i>=0;i--){ System.out.println(i); }
scala版: for(i <- 1 to 10 reverse) println(i)
5、编写一个过程 countdown(n: Int) ,打印从n到0的数字。 def answer5(n: Int): Unit ={ for(i <- 0 to n reverse){ print(i + " ") } }
6、编写一个for循环,计算字符串中所有字母的Unicode代码的乘积。 def answer6(str: String): Long = { var res: Long = 1 for(c <- str){ res *= c.toLong } println(s"$str count value is: " + res) res } def main(args: Array[String]): Unit = { answer6("Hello") }
7、同样是问题6,但这次不允许使用循环语句。
查找到 def foreach(f: (A) ⇒ Unit): Unit [use case] Applies a function f to all elements of this string.
因此可以考虑使用foreach方法计算。 def answer7(str: String): Long = { var res: Long = 1 str.foreach(c => res *= c.toLong) println(s"$str count value is: " + res) res } def main(args: Array[String]): Unit = { answer7("Hello") }
8、编写一个函数product(s: String),计算前面练习中提到的乘积。 def product(str: String): Long = { var res: Long = 1 str.foreach(c => res *= c.toLong) println(s"$str count value is: " + res) res }
9、把前一个练习中的函数改造为递归函数 def answer9(str: String): Long = { if(str.length == 1){ str(0).toLong }else{ // 选择第0个元素,返回除了第0个元素的其他元素 str(0).toLong * answer9(str.drop(1)) } } def main(args: Array[String]): Unit = { val ans = answer9("Hello") print(ans) }
其中Doc文档: // 返回除了前n个节点的元素 def drop(n: Int): String Selects all elements except first n ones. // 并没有用到此方法 def take(n: Int): String Selects first n elements. // 默认方法 apply def apply(index: Int): Char Return element at index n
10、编写函数计算$x^n$,其中n是整数,使用如下的递归定义 $x^n=y*y$,如果n是正偶数的话,这里的$y=x^{\frac{n}{2}}$; $x^n=x*x^(n-1)$,如果n是正奇数的话; $x^0=1$ $x^n=\frac{1}{x^{-n}}$,如果n是负数的话。 def answer10(x: Double, n: Int): Double = { if(n == 0) 1 else if (n > 0 && n % 2 == 0) answer10(x,n/2) * answer10(x,n/2) else if (n > 0 && n % 2 == 1) x * answer10(x,n - 1) else 1 / answer10(x, -n) } def main(args: Array[String]): Unit = { val ans = answer10(2,2) print(ans) // 4.0 }
人工智能
2019-02-22 19:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
近日,权威ICT市场咨询机构计世资讯(CCW Research)发布《2018-2019年中国CDN市场发展报告》,报告显示,当前,随着新型信息技术在中国不断应用,以及互联网化新业务的快速发展,特别是物联网、边缘计算、区块链、人工智能等技术在游戏、视频、电子商务、工业制造、交通物流等行业的落地,使得CDN作为缓解互联网网络拥塞、提高互联网业务响应速度、改善用户业务体验的重要手段,已经成为互联网基础设施中不可或缺的重要组成部分。
近年来,主要CDN服务提供商大幅增加节点覆盖,带宽储备飞速增长。 截至 2018年底,阿里、网宿、腾讯等CDN 服务提供商业务已经覆盖了国内所有省份,其中在华北、华东、华南、华中等地区分布较为密集。已建成各类CDN节点数超过 5000 个,据计世资讯(CCW Research)统计,国内主要CDN总峰值带宽同比增长 82.5%。
从业务类型来看,2018-2019年,中国CDN市场传统的业务如页面和视频加速仍为当前主要业务流量和收入来源。从CDN业务日均流量上看,视频业务流量最多,占比 61.2%。
同时,除了传统CDN业务之外,一些CDN服务提供商开始积极探索对新兴业务的专业CDN服务,如七牛云的视频云解决方案、金山云的人工智能解决方案、白山云的工业大数据分析解决方案等,为客户提供更全面的一站式、定制化服务。
2018年中国CDN市场规模增长81.8%
互联网和移动互联网的快速发展,带动了多媒体内容的不断丰富、应用数量的激增、物联网的发展,流量的爆发,大幅推动了对CDN的需求。随着视频直播等高耗能业务的推动,云计算技术的落地和虚拟现实(VR)等概念的推广,CDN领域进入了新一轮高速发展的轨道。2018年,视频点播和直播应用、电子商务等成为CDN持续高增长的源动力。据计世资讯(CCW Research)统计显示,2018年中国CDN市场规模为201.6亿元,比2017年增长81.8%。

2016-2018年中国CDN市场规模及增长率
根据计世资讯(CCW Research)研究显示,2019年以后,随着企业数字化转型和各产业信息化工程的深入建设,以互联网、政府、金融等行业等为代表的用户开始进入提质增效、业务拓展的关键阶段,同时,随着5G等新兴通讯技术的应用,我国网络建设也将进入一个新的阶段,人工智能、物联网应用规模和深度将不断增加,用户对网络体验的需求也将持续增强,对CDN技术的需求也会提升一个层级,2019-2022年期间CDN市场将保持高速增长,预计2019年中国CDN市场规模将达到355.1亿元,而随着新兴信息技术的不断演进,CDN将与云安全、人工智能、边缘计算、物联网、视频云、大数据等解决方案进一步融合,CDN将成为支撑这些技术落地应用的重要网络平台,使得CDN市场的内涵和外延不断扩大,拉动市场规模迅速扩充,预计到2022年,市场规模将达到1358.6亿。
2019-2022年中国CDN市场规模及增长率预测
2018年互联网巨头占据市场主流,未来综合解决方案提供商占据市场主流

2018年中国CDN市场份额
2018年,在中国CDN市场中,阿里云、网宿科技、腾讯云位列前三位,成为CDN市场的第一集团,市场份额接近70%;金山云、白山云、七牛云和蓝汛是第二集团。

中国CDN解决方案提供商发展态势图
阿里云凭借覆盖全球的1500多个节点以及120T带宽的能力,成为中国CDN市场的领军者;网宿科技近年来除了不断强化传统CDN业务之外,还向云安全、边缘计算等领域发展,取得了一定的成绩;腾讯云CDN拥有覆盖全球的1300多个自建节点及超过10T的网络资源,近年来不断发力高度定制化应用场景,为业务添加独特的内容分发功能,巩固了市场地位。
金山云、白山云和七牛云等提供商则结合用户需求及自身技术方案特点,向客户提供更有针对性合功能性的产品和解决方案,如七牛云的融合CDN (FUSION),在传统 CDN 基础上实现对数据网络加速进一步优化的融合管理,服务于音视频点播,文件、应用与 Web 加速。也取得不错的成绩。
未来,随着行业用户数字化转型的深入,CDN将会成为帮助企业实现智能网络,推动IT基础架构云化转型重要使能技术,而随着物联网理念的演进,基于网络技术衍生出的附加需求,如大数据分析、边缘计算、视频云等将进一步加大,用户对CDN提供商的要求也将会从单纯的加速向基于新一代智能网络技术的综合服务提供商转变。
计世资讯(CCW Research)是工业和信息化部电子科学技术情报研究所下属专业研究咨询机构,中国ICT领域权威咨询与服务平台,是国家工业和信息化部等政府部门的重要研究支撑机构。
原文链接
人工智能
2019-02-22 17:40:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1 CentOS上安装中需要配置第三方源
# s udo yum install epel-release
如果不安装第三方软件源,很多依赖包都不能通过yum安装
Makefile
blas = open

2 Unbuntu
PYTHON_INCLUDES=/usr/local/lib/python2.7/dist-packages/...
人工智能
2019-02-22 15:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
数据测试 是 功能测试 的 第二个特征:
表现如下 :
1,结构明确、
2关联关系复杂
3,数据的变换依赖于 功能的业务;
要测试数据,应该遵循 这几个方面
1,业务单独隔离开,比如你测试 添加一个用户 那么你就只看用户的数据;
2,业务逻辑 隔离 ,方法同上
3,业务关系 落地于 先看结果,在看业务逻辑;
4,DB文档和DB的完整性、一致性;
5,业务sql 的剔除性;说白了 就是拿到 开发代码里面的sql 按照你的 业务理解 进行sql 数据的查询 和 验证,是否有脏数据;
人工智能
2019-02-22 14:32:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
摘要: 复杂的深度模型中,如果效果不好,是因为网络设计的欠缺?还是数据天然缺陷?是训练代码的bug?还是Tensorflow自身的问题?基于此,阿里工程师推出了DeepInsight深度学习质量平台,致力于解决当前模型调试和问题定位等一系列问题。
小叽导读:复杂的深度模型中,如果效果不好,是因为网络设计的欠缺?还是数据天然缺陷?是训练代码的bug?还是Tensorflow自身的问题?基于此,阿里工程师推出了DeepInsight深度学习质量平台,致力于解决当前模型调试和问题定位等一系列问题。接下来,阿里巴巴高级技术专家、DeepInsight深度学习质量平台技术负责人:孙凯(花名:路宸),带我们一起探索。
1. 背景
机器学习训练过程的调试、可视化以及训练效果的评估一直是业界难题。在数据较少,模型较简单,如LR、GBDT、SVM,超参不多的情况下,模型的可调性和可解释性都有一定保障,那么我们用简单的训练,再观察召回/精度/AUC等指标就可以应对。
而深度学习时代,模型的复杂性远远超乎想象,层层嵌套的网络结构,优化器和大量超参的选择,特征的连续化,一起构建了复杂的深度模型。如果效果不好,其原因是多样的,为了定位和解决这些问题,算法研发同学需要花费大量精力反复尝试,而且很可能得不到准确的答案。简单来说,网络模型近似于黑盒。

DeepInsight
通过研究,我们发现训练和评估过程中大量中间指标与模型效果能产生关系,通过系统的分析建模张量、梯度、权重和更新量,能够对算法调优、问题定位起到辅助决策作用。而且,通过改进AUC算法,分析ROC、PR、预估分布等更多评估指标,能够更全面地评估模型效果。
通过2个多月的努力,我们推出了DeepInsight平台,致力于解决当前模型调试和问题定位等一系列问题。提交模型开始训练之后,用户可以通过DeepInsight平台,能一站式查看并分析训练过程,从训练中间指标到预测指标,再到性能数据,一应俱全。对于训练中明显的问题,平台也会高亮给予提示。未来,我们希望平台能更好地帮助用户发现和定位训练中的问题,并能给予适当提示(如更改某些子网络的最优化算法、更改学习率动量等),就如同GDB之于C++一样。
2.1 目标
沉淀并持久化训练数据。深度学习的数据非常宝贵,每次训练的网络拓扑、参数、训练中间过程、模型评估指标都会持久存储,方便后续人工分析和二次建模;
沉淀对模型训练的认识,提供分析调优手段,辅助决策,同时规避各类已知问题;
利用大数据分析建模,寻找中间过程指标的关系,更好地辅助决策,我们称这个目标为Model on Model,即利用新的模型来分析评估深度模型;
在大数据分析建模的基础上,尝试对已有模型进行深度强化学习(DRL),提高深度学习调试效率。
2.2 架构
系统主要分为四层:输入层、解析层、评估层、输出层;
同时包括五大组件:Tensorboard+可视化分析;TensorViewer日志展示对比;TensorDealer集成配置;TensorTracer数据透出;TensorDissection分析调优。
2.3 进展
2.3.1 高性能可视化组件TensorBoard+
Google的TensorBoard(简称为TB)是TensorFlow(简称为TF)的可视化组件,可以查看深度学习的网络结构、中间指标等。原生的TB是单机版命令行方式运行,无法多用户使用;易用性差,每次切换日志路径都需要kill掉当前进程;同时性能也很差,加载工业模型数据立即卡死;指标分层混乱,几千个指标全都罗列,无法查看;用法复杂功能较弱,不支持已展示图形的二次数据对比,不支持X轴浮点数据展示等。
因此,我们重构了TB的核心代码,支持GB级日志加载和数据分层,将整个服务改造成多用户版本,利用Docker灵活管理资源并自动回收。UI上支持了高亮自定义指标、分层展示、数据对比、日志上传等,具体如下:
支持在线更改TF日志路径:
支持图形数据在线聚合对比:
支持X轴浮点数值类型展示:

支持图形数据Hightlight分维度显示:
支持手动调整前端定时刷新时间,实时展示数据:
2.3.2 集成配置日志管理系统TensorViewer
TF的任务缺乏有效管理,用户无法按需查看和分析数据,更无法回顾历史数据。我们打通了TF与DeepInsight的通路,收集了所有任务的信息,用户可以查看每次训练的实时数据和所有历史数据,支持多任务对比分析;同时支持一键跳转到Tensorboard+,直接对当前日志数据进行可视化展示。

2.3.3 改进TensorFlow的可视化数据透出
我们定义了一套数据透出方式,可以把所有内部数据透出成统一的Summary格式,并被Tensorboard+处理。由于PS架构没有Master集中处理中间数据,再加上张量、梯度等指标的透出是极为消耗资源的,所以,如何透出数据是值得深入研究的。当前我们在Worker0上透出数据,能满足一般模型训练的要求,未来,会研究Snapshot数据透出方案,在大规模网络下也能取得较好效果。
当前,我们已经初步解析了Tensorflow透出的过程指标,正在这些海量指标上进行有监督和无监督的建模探索。
2.3.4 改进模型评估指标
Tensorflow自带的AUC计算方式分桶较少,计算精度有bug,在处理大量数据时性能不够,而且,仅仅能计算AUC,无法绘制ROC、PR等曲线。
我们改进了计算方式,引入更多桶,并提升计算效率,同时,绘制了更多新的指标。当前绘制的指标包括AUC、ROC、PR、波动率、正负样本分桶分布。通过观察正负样本的分布,我们发现Tensorflow异步计算的缺陷,导致某些桶的样本数量有误差,会带来AUC上极小波动,这个bug目前尚未解决。所有的预估指标都无缝接入DeepInsight平台。
2.3.5 研究模型训练中间指标
通过深入观察和建模大规模Embedding子网络的训练指标,我们发现权重(偏置)值的变化可以反应出相关网络结构是否被有效训练。权重(偏置)值变化微弱的区域即为训练的“盲区”—该部分网络没有被训练起来。通过观察权重(偏置)的梯度,可以帮助我们诊断梯度弥散或梯度爆炸等问题,分析了解训练该部分网络的难易程度,有针对性地调整优化器以及学习率等设置。通过全面考察整个网络各部分的激活以及梯度,可以帮助我们深入了解整个网络前后向多路信息相互耦合、协同传导的复杂机制,从而更有效地进行模型结构的设计调优。
对中间指标的研究会沉淀回流到DeepInsight,在训练指标产出后,对用户给予提示,做到辅助决策的作用。
原文链接
人工智能
2019-02-22 10:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1.背景
ETA(Estimated Time of Arrival,“预计送达时间”),即用户下单后,配送人员在多长时间内将外卖送达到用户手中。送达时间预测的结果,将会以"预计送达时间"的形式,展现在用户的客户端页面上,是配送系统中非常重要的参数,直接影响了用户的下单意愿、运力调度、骑手考核,进而影响配送系统整体成本和用户体验。
对于整个配送系统而言,ETA既是配送系统的入口和全局约束,又是系统的调节中枢。具体体现在: ETA在用户下单时刻就需要被展现,这个预估时长继而会贯穿整个订单生命周期,首先在用户侧给予准时性的承诺,接着被调度系统用作订单指派的依据及约束,而骑手则会按照这个ETA时间执行订单的配送,配送是否准时还会作为骑手的工作考核结果。 ETA作为系统的调节中枢,需要平衡用户-骑手-商家-配送效率。从用户的诉求出发,尽可能快和准时,从骑手的角度出发,太短会给骑手极大压力。从调度角度出发,太长或太短都会影响配送效率。而从商家角度出发,都希望订单被尽可能派发出去,因为这关系到商家的收入。
在这样多维度的约束之下,外卖配送的ETA的建模和估计会变得更加复杂。与打车场景中的ETA做对比,外卖场景的ETA面临如下的挑战: 外卖场景中ETA是对客户履约承诺的重要组成部分,无论是用户还是骑手,对于ETA准确性的要求非常高。而在打车场景,用户更加关心是否能打到车,ETA仅提供一个参考,司机端对其准确性也不是特别在意。 由于外卖ETA承担着承诺履约的责任,因此是否能够按照ETA准时送达,也是外卖骑手考核的指标、配送系统整体的重要指标;承诺一旦给出,系统调度和骑手都要尽力保证准时送达。因此过短的ETA会给骑手带来极大的压力,并降低调度合单能力、增加配送成本;过长的ETA又会很大程度影响用户体验。 外卖场景中ETA包含更多环节,骑手全程完成履约过程,其中包括到达商家、商家出餐、等待取餐、路径规划、不同楼宇交付等较多的环节,且较高的合单率使得订单间的流程互相耦合,不确定性很大,做出合理的估计也有更高难度。
下图是骑手履约全过程的时间轴,过程中涉及各种时长参数,可以看到有十几个节点,其中关键时长达到七个。这些时长涉及多方,比如骑手(接-到-取-送)、商户(出餐)、用户(交付),要经历室内室外的场景转换,因此挑战性非常高。对于ETA建模,不光是简单一个时间的预估,更需要的是全链路的时间预估,同时更需要兼顾"单量-运力-用户转化率"转化率之间的平衡。配送ETA的演变包括了数据、特征层面的持续改进,也包括了模型层面一路从LR-XGB-FM-DeepFM-自定义结构的演变。
具体ETA在整个配送业务中的位置及配送业务的整体机器学习实践,请参看 《机器学习在美团配送系统的实践:用技术还原真实世界》 。
2.业务流程迭代中的模型改进
2.1 基础模型迭代及选择
与大部分CTR模型的迭代路径相似,配送ETA模型的业务迭代经历了LR->树模型->Embedding->DeepFM->针对性结构修改的路径。特征层面也进行不断迭代和丰富。 模型维度从最初考虑特征线性组合,到树模型做稠密特征的融合,到Embedding考虑ID类特征的融合,以及FM机制低秩分解后二阶特征组合,最终通过业务指标需求,对模型进行针对性调整。 特征维度逐步丰富到用户画像/骑手画像/商家画像/地址特征/轨迹特征/区域特征/时间特征/时序特征/订单特征等维度。
目前版本模型在比较了Wide&Deep、DeepFM、AFM等常用模型后,考虑到计算性能及效果,最终选择了DeepFM作为初步的Base模型。整个DeepFM模型特征Embedding化后,在FM(Factorization Machine)基础上,进一步加入deep部分,分别针对稀疏及稠密特征做针对性融合。FM部分通过隐变量内积方式考虑一阶及二阶的特征融合,DNN部分通过Feed-Forward学习高阶特征融合。模型训练过程中采取了Learning Decay/Clip Gradient/求解器选择/Dropout/激活函数选择等,在此不做赘述。
2.2 损失函数
在ETA预估场景下,准时率及置信度是比较重要的业务指标。初步尝试将Square的损失函数换成Absolute的损失函数,从直观上更为切合MAE相比ME更为严苛的约束。在适当Learning Decay下,结果收敛且稳定。
同时,在迭代中考虑到相同的ETA承诺时间下,在前后N分钟限制下,早到1min优于晚到1min,损失函数的设计希望整体的预估结果能够尽量前倾。对于提前部分,适当降低数值惩罚。对于迟到部分,适当增大数值惩罚。进行多次调试设计后,最终确定以前后N分钟以及原点作为3个分段点。在原先absolute函数优化的基础上,在前段设计1.2倍斜率absolute函数,后段设计1.8倍斜率absolute函数,以便让结果整体往中心收敛,且预估结果更倾向于提前送达,对于ETA各项指标均有较大幅度提升。
2.3 业务规则融入模型
目前的业务架构是"模型+规则",在模型预估一个ETA值之后,针对特定业务场景,会有特定业务规则时间叠加以满足特定场景需求,各项规则由业务指标多次迭代产生。这里产生了模型和规则整体优化的割裂,在模型时间和规则时间分开优化后,即模型训练时并不能考虑到规则时间的影响,而规则时间在一年之中不同时间段,会产生不同的浮动,在经过一段时间重复迭代后,会加大割裂程度。
在尝试了不同方案后,最终将整体规则写入到了TF模型中,在TF模型内部调整整体规则参数。 对于简单的(a*b+c)*d等规则,可以将规则逻辑直接用TF的OP算子来实现,比如当b、d为定值时,则a、c为可学习的参数。 对于过于复杂的规则部分,则可以借助一定的模型结构,通过模型的拟合来代替,过多复杂OP算子嵌套并不容易同时优化。
通过调节不同的拟合部分及参数,将多个规则完全在TF模型中实现。最终对业务指标具备很大提升效果,且通过对部分定值参数的更改,具备部分人工干涉模型能力。
在这里,整体架构就简化为多目标预估的架构,这里采用多任务架构中常用的Shared Parameters的结构,训练时按比例采取不同的交替训练策略。结构上从最下面的模型中间融合层出发,分别在TF内实现常规预测结构及多个规则时间结构,而其对应的Label则仍然从常规的历史值和规则时间值中来,这样考虑了以下几点: 模型预估时,已充分考虑到规则对整体结果的影响(例如多个规则的叠加效应),作为整体一起考虑。 规则时间作为辅助Label传入模型,对于模型收敛及Regularization,起到进一步作用。 针对不同的目标预估,采取不同的Loss,方便进行针对性优化,进一步提升效果。
模型结构在进行预估目标调整尝试中: 尝试过固定共享网络部分及不固定共享部分参数,不固定共享参数效果明显。 通常情况下激活函数差异不大,但在共享层到独立目标层中,不同的激活函数差异很大。
2.4 缺失值处理
在模型处理中,特征层面不可避免存在一定的缺失值,而对于缺失值的处理,完全借鉴了 《美团“猜你喜欢”深度学习排序模型实践》 文章中的方法。对于特征x进入TF模型,进行判断,如果是缺失值,则设置w1参数,如果不是缺失值则进入模型数值为w2*x,这里将w1和w2作为可学习参数,同时放入网络进行训练。以此方法来代替均值/零值等作为缺失值的方法。
3.长尾问题优化
3.1 模型预估结果+长尾规则补时
基础模型学习的是整体的统计分布,但对于一些长尾情形的学习并不充分,体现在长尾情形下预估时间偏短(由于ETA拥有考核骑手的功能,预估偏短对骑手而言意味着很大的伤害)。故将长尾拆解成两部分来分析: 业务长尾,即整体样本分布造成的长尾。主要体现在距离、价格等维度。距离越远,价格越高,实际送达时间越长,但样本占比越少,模型在这一部分上的表现整体都偏短。 模型长尾,即由于模型自身对预估值的不确定性造成的长尾。模型学习的是整体的统计分布,但不是对每个样本的预估都有“信心”。实践中采用RF多棵决策树输出的标准差来衡量不确定性。RF模型生成的决策树是独立的,每棵树都可以看成是一个专家,多个专家共同打分,打分的标准差实际上就衡量了专家们的“分歧”程度(以及对预估的“信心”程度)。从下图也可以看出来,随着RF标准差的增加,模型的置信度和准时率均在下降。
在上述拆解下,采用补时规则来解决长尾预估偏短的问题:长尾规则补时为 <业务长尾因子 , 模型长尾因子> 组合。其中业务长尾因子为距离、价格等业务因素,模型长尾因子为RF标准差。最终的ETA策略即为模型预估结果+长尾规则补时。
4.工程开发实践
4.1 训练部分实践
整体训练流程
对于线下训练,采取如下训练流程:
Spark原始数据整合 -> Spark生成TFRecord -> 数据并行训练 -> TensorFlow Serving线下GPU评估 -> CPU Inference线上预测
整个例行训练亿级数据多轮Epoch下流程持续约4小时,其中TF训练中,考虑到TF实际计算效率并不是很高,有很大比例在数据IO部分,通过Spark生成TFRecord部分,在此可将速度加速约3.6倍。而在数据并行训练部分,16卡内的并行度扩展基本接近线性,具备良好的扩展性。由于PS上参数量并未达到单机无法承受,暂时未对参数在PS上进行切分。Serving线下GPU评估部分,是整个流程中的非必需项,虽然在训练过程中Chief Worker设置Valid集合可有一定的指标,但对全量线下,通过Spark数据调用Serving GPU的评估具备短时间内完成全部流程能力,且可以指定大量复杂自定义指标。
数据并行训练方式
整个模型的训练在美团的AFO平台上进行,先后尝试分布式方案及单机多卡方案。考虑到生产及结果稳定性,目前线上模型生产采用单机多卡方案进行例行训练。 分布式方案:
采用TF自带的PS-Worker架构,异步数据并行方式,利用 tf.train.MonitoredTrainingSession 协调整个训练过程。整个模型参数存储于PS,每个Step上每个Worker拉取数据进行数据并行计算,同时将梯度返回,完成一次更新。目前的模型单Worker吞吐1~2W/s,亿级数据几轮Epoch耗时在几小时内完成。同时测试该模型在平台上的加速比,大约在16块内,计算能力随着Worker数目线性增加,16卡后略微出现分离。在目前的业务实践中,基本上4-6块卡可以短时间内完成例行的训练任务。 单机多卡方案:
采用PS-Worker的方案在平台上具备不错的扩展性,但是也存在一定的弊端,使用RPC的通讯很容易受到其他任务的影响,整个的训练过程受到最慢Worker的影响,同时异步更新方式对结果也存在一定的波动。对此,在线上生产中,最终选取单机多卡的方案,牺牲一定的扩展性,带来整体训练效果和训练速度的稳定性。单机多卡方案采取多GPU手动指定OP的Device,同时在各个Device内完成变量共享,最后综合Loss与梯度,将Grad更新到模型参数中。
TF模型集成预处理
模型训练过程中,ID类特征低频过滤需要用到Vocab词表,连续型特征都需要进行归一化。这里会产生大量的预处理文件,在线下处理流程中很容易在Spark中处理成Libsvm格式,然后载入到模型中进行训练。但是在线上预测时,需要在工程开发端载入多个词表及连续型特征的归一化预处理文件(avg/std值文件等),同时由于模型是按天更新,存在不同日期版本的对齐问题。
为了简化工程开发中的难度,在模型训练时,考虑将所有的预处理文件写入TF计算图之中,每次在线预测只要输入最原始的特征,不经过工程预处理,直接可得到结果: 对于ID类特征,需要进行低频过滤,然后制作成词表,TF模型读入词表的list_arr,每次inference通过ph_vals,得到对应词表的ph_idx。 tf_look_up = tf.constant(list_arr, dtype=tf.int64) table = tf.contrib.lookup.HashTable(tf.contrib.lookup.KeyValueTensorInitializer(tf_look_up, idx_range), 0) ph_idx = table.lookup(ph_vals) + idx_bias 对于连续型特征,在Spark处理完得到avg/std值后,直接写入TF模型计算图中,作为constant节点,每个ph_in经过两个节点,得到相应ph_out。 constant_avg = tf.constant(feat_avg, dtype=tf.float32, shape=[feat_dim], name="avg") constant_std = tf.constant(feat_std, dtype=tf.float32, shape=[feat_dim], name="std") ph_out = (ph_in - constant_avg) / constant_std
4.2 TF模型线上预测
配送机器学习平台内置了模型管理平台,对算法训练产出的模型进行统一管理和调度,管理线上模型所用的版本,并支持模型版本的切换和回退,同时也支持节点模型版本状态的管理。
ETA使用的DeepFM模型用TensorFlow训练,生成SavedModel格式的模型,需要模型管理平台支持Tensorflow SavedModel格式。
实现方案 S 线上服务加载TensorFlow SavedModel模型有多种实现方案: 自行搭建TensorFlow Serving CPU服务,通过gRPC API或RESTful API提供服务,该方案实现比较简单,但无法与现有的基于Thrift的模型管理平台兼容。 使用美团AFO GPU平台提供的TensorFlow Serving服务。 在模型管理平台中通过JNI调用TensorFlow提供的Java API TensorFlow Java API ,完成模型管理平台对SavedModel格式的支持。
最终采用TensorFlow Java API加载SavedModel在CPU上做预测,测试batch=1时预测时间在1ms以内,选择方案3作为实现方案。
远程计算模式
TensorFlow Java API的底层C++动态链接库对libstdc++.so的版本有要求,需要GCC版本不低于4.8.3,而目前线上服务的CPU机器大部分系统为CentOS 6, 默认自带GCC版本为4.4.7。如果每台线上业务方服务器都支持TensorFlow SavedModel本地计算的话,需要把几千台服务器统一升级GCC版本,工作量比较大而且可能会产生其他风险。
因此,我们重新申请了几十台远程计算服务器,业务方服务器只需要把Input数据序列化后传给TensorFlow Remote集群,Remote集群计算完后再将Output序列化后返回给业务方。这样只需要对几十台计算服务器升级就可以了。
线上性能
模型上线后,支持了多个业务方的算法需求,远程集群计算时间的TP99基本上在5ms以内,可以满足业务方的计算需求。
总结与展望
模型落地并上线后,对业务指标带来较大的提升。后续将会进一步根据业务优化模型,进一步提升效果: 将会进一步丰富多目标学习框架,将取餐、送餐、交付、调度等整个配送生命周期内的过程在模型层面考虑,对订单生命周期内多个目标进行建模,同时提升模型可解释性。 模型融合特征层面的进一步升级,在Embedding以外,通过更多的LSTM/CNN/自设计结构对特征进行更好的融合。 特征层面的进一步丰富。
作者简介 基泽,美团点评技术专家,目前负责配送算法策略部机器学习组策略迭代工作。 周越,2017年加入美团配送事业部算法策略组,主要负责ETA策略开发。 显杰,美团点评技术专家,2018年加入美团,目前主要负责配送算法数据平台深度学习相关的研发工作。
人工智能
2019-02-22 10:38:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
背景
之前写过浅谈神经网络基础篇,简单介绍下机器学习这块内容,用于扫盲。本文正式将神经网络,这部分是深度学习的基础。了解完可以掌握强大的机器学习的方法,也可以更好的了解深度学习。本文会从历史维度来讲讲神经网络的发展史,以便于更好的理解神经网络的由来以及为什么这样子。
神经网络
基础概念
神经网络是一种模拟人脑的神经网络以期能够实现类人工智能的机器学习技术。
如图所示是一个经典的神经网络图片。 左边红色是输入层,包含三个输入单元。 中间是隐藏层,中间有四个计算节点。 右边是输出层,带有两个输出单元。
输入层的输入单元个数应该是等于输入的特征个数,而输出层则是等于需要的结果标签个数,而对于中间层可以自由指定。例如: 特征:叫声|眼睛|体型 结果标签:猫|狗
另外图中重要的并不是每个节点,而是每条线。每条线代表着一定的权重,神经网络的训练算法就是让权重的值调整到最佳,以使得整个网络的预测效果最好。
下面来介绍下神经网络是怎么发展到现在这样的结构图的。
神经元
神经网络的最小单位便是神经元,早在1904年的时候生物学家就已经掌握了神经元的结构。
一个神经元通常具有多个树突,主要用来接受传入信息。 中间仅存在一条长长的轴突,轴突尾端有许多轴突末梢。 轴突末梢跟其他神经元的树突产生连接,从而传递信号。
1943年一名心理学家和一名数学家参考了生物神经元的结构,发表了抽象的神经元模型MP。神经元模型是一个包含输入和输出以及计算功能的模型。输入可以类比为神经元的树突,而输出可以类比为神经元的轴突,计算则可以类比为细胞核。如下图所示。
如果要用公式表示这个神经元的计算公式的话,假设: 输入为x(图中为x1 x2 x3) 权值为w(图中为w1 w2 w3) 非线性函数为f(x) 输出结果为y
那么我们就得到公式: y=f(x1*w1+x2*w2+x3*w3)
这里MP模型里面非线性函数为sgn函数(这是个取符号函数,当输入> 0 输出1,否则 -1)。
具体的MP模型如下:
从上面的公式其实可以看的出,假如输入的特征跟输出的结果是线性关系,且你也知道各个权重的值,那么只需要把特征带入公式就可以直接得到预测的结果。这个模型虽然看着简单,但这也是神经网络的基础。当时存在的问题就是权重都是预先设置的,所以不具备学习能力,且受限于当时计算机的硬件水平(晶体管)。
感知机(单层神经网络)
1958年,一位计算科学家提出了由两层神经元组成的神经网络(当时第一个可以学习的神经网络),取名为感知机。当时由于这位计算科学家现场演示了识别简单图片的能力,当时引起了很大的反响(第一次高潮)。
感知机的模型是怎么样的呢?基于神经元的基础之上,划分了两个层次,分别是输入层和输出层,输入层只负责传入特征不做计算,而对于输出层的输出单元要根据输入层进行计算输出。(由于这里计算层只有一个,也就是输出层,所以称为单层神经网络)
从图中可以看出两个输出为z1 z2,输入为a1 a2 a3,g函数表示为sgn函数(MP模型所用的非线性函数)。 可以发现z1和z2就是线性代数方程组,输入向量为[a1,a2,a3]输出向量为[z1,z2],那么w就是个2行3列的矩阵。可以得到公式: g(W * a) = z;
那么这个公式解决什么问题,其实感知机是个逻辑回归模型(跟线性回归不同,线性回归是做预测。逻辑回归主要做分类)。所以我们可以做简单的线性分类任务,类似于边界一样的抽象概念(二维中就是一条线,三维中就是一个面)。
当然,感知机也只能是做简单的线性分类任务,无法解决异或的问题(XOR)。如图所示,感知机没法在二维上画一条直线把X和O进行分类。所以神经网络的研究又开始凉凉。
多层感知机(两层神经网络)
多层感知机的发展带动了神经网络的推广和使用。上面说到感知机无法解决XOR的问题。但是继续对感知机多加一个计算层后,两层的神经网络就可以解决异或问题,且具备了非线性的分类效果。但是当时对于两层的神经网络计算没有一个很好的解决办法,直到1987年反向传播(BP)算法被提出,解决了两层神经网络复杂计算量的问题。
两层神经网络除了包含一个输入层,一个输出层以外,还增加了一个中间层。此时,中间层和输出层都是计算层。我们基于单层神经网络多加一层计算层可以看到下面这张图:
最终输出节点的公式则为:
当然,这里输出节点只有一个z,假如我们有多个输出节点,那么如下所示:
输入输出矩阵公式为:(这里的a(1)代表的是所有输入向量,w(1)是权重向量,以此类推) g(W(1) * a(1)) = a(2); g(W(2) * a(2)) = z;
从上面讨论到现在,其实还少了偏置节点这个说法,但是这些节点默认是存在的,本质上偏置节点是一个含有存储功能,且存储值为1的单元。除了输出层以外,都会有这个偏置节点存在。我们假设偏置值用b来表示,那么图示如下:
新的矩阵计算公式如下: g(W(1) * a(1) + b(1)) = a(2); g(W(2) * a(2) + b(2)) = z;
默认情况下,神经网络结果图并不会把偏置节点画出。而且到了两层神经网络以后,激活函数使用sigmoid 平滑函数来作为激活函数(也就是非线性函数)。
关于多层感知机为什么能够解决分线性分类问题
为什么面对复杂的非线性分类任务,两层神经网络效果会更好。主要原因在于中间隐藏层,从输入层到隐藏层时,数据发生了空间变换。对之前像XOR这种线性不可分的问题,进行了坐标变换,使其线性可分(简单理解可以说是特征组合生成了新的特征,生成特征抽象程度更高)。两层神经网络通过两层的线性模型模拟了数据内真实的非线性函数。
接下来讨论下关于神经网络输入层/输出层/中间层的设计。在设计一个神经网络时,输入层的节点数需要与特征的维度匹配,输出层的节点数要与目标的维度匹配。而中间层的节点数,把握在设计者的手中,节点数设置的多少,却会影响到整个模型的效果。目前业界都是通过经验来设置这个值,所以这个需要实践过程中调参数获取。
反向传播算法
反向传播算法的推出为什么带动了神经网络的推广使用,主要是因为在神经网络模型中结构复杂,每次计算训练过程中的梯度的代价很大,反向传播解决了在评估损失调节参数上的问题。
关于泛化和正则化
这个话题在基础篇中介绍过,因为模型最终上到真实场景,所以很有必要提升模型在测试集上的预测效果,那么就需要对训练数据要进行很好的泛化(神经网络会涉及到一些泛化技术),且使用一些正则化方法来评估模型复杂度。
多层感知机带来的变化
BP算法出现以及计算机硬件发展,神经网络界的异或问题被轻松解决且已经可以开始做语音识别/图像识别等领域相关的事情。虽然用了BP算法解决了梯度问题,但是神经网络的一次训练耗时太久,也因为训练优化中存在局部最优解问题影响神经网络优化,且隐藏层的调参麻烦。所以使用并不是那么方便,所以又开始凉凉,一直到多层神经网络发展。
深度学习(多层神经网络)
其实讲完多层感知机,基本神经网络的很大的内容已经在多层感知机(两层神经网络)上了。而多层神经网络是建立在两层神经网络上的一种升华。
2006年“深度信念网络”概念的提出,减少了多层神经网络的训练时间。深度信念网络有两个技术: “预训练”(pre-training):更加方便让神经网络中的权值找到一个接近最优解的值。 “微调”(fine-tuning):通过最优解来对整个神经网络进行优化训练。
所以深度学习在语音图像识别上效果越发优越。基于上面多层感知机,我们继续增加多一层计算层如下图所示:
那么矩阵运算推导公式为: g(W(1) * a(1)) = a(2); g(W(2) * a(2)) = a(3); g(W(3) * a(3)) = z;
这种计算不断增加层数,计算不断往前推进的方式称为“正向传播”。随着层数的增多,那么特征经过每一层计算后,会被抽象得更加具体。如图所示:
更抽象的特征可以理解为:随着网络的层数增加,每一层对于前一层次的抽象表示更深入。在神经网络中,每一层神经元学习到的是前一层神经元值的更抽象的表示。例如第一个隐藏层学习到的是“边缘”的特征,第二个隐藏层学习到的是由“边缘”组成的“形状”的特征,第三个隐藏层学习到的是由“形状”组成的“图案”的特征,最后的隐藏层学习到的是由“图案”组成的“目标”的特征。通过抽取更抽象的特征来对事物进行区分,从而获得更好的区分与分类能力。理论如下:
当特征抽象得越高级越具体,那么就越逼近于我们想要的结果。本质就是通过已有的数据去不断拟合复杂函数。
总结
要了解神经网络必然要对神经网络历史的发展有所了解才能够很清楚的明白神经网络是怎么一步步的去解决现实问题,以至于最后用到语音图像识别上的,而且神经网络的发展也离不开计算机算力的提高。但是这里还是强调下神经网络只是机器学习的一部分,还有些问题其实用其他方法会更加高效,神经网络比较适合图像音频这类的识别。最后神经网络真实的名字为前馈神经网络,具体神经网络划分如下:
关于使用深度学习框架要纠正的点
因为目前深度学习框架都提供了高级api,所以我们设计神经网络一般都考虑多少层以及层的节点个数。而并不是一个神经元节点一个一个去拼成一个拓扑图,这是初学者使用深度学习框架入门很容易陷进去的误区。第三篇介绍神经网络的文章,我将会找个框架,简单介绍下怎么用代码来使用神经网络做一些有趣的事情。
人工智能
2019-02-22 01:50:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
本文将展示如何使用开源工具完成一个人脸识别的算法。

引言
“计算机视觉和机器学习已经开始腾飞,但是大多数人并不清楚计算机在识别一张图片的时候,它到底看到了什么。”——麦克.克里奇

计算机视觉这个精彩领域在最近几年突飞猛进,目前已经具备了一定的规模。大量的应用已经在全世界被广泛使用 —— 而这也仅仅是个开始!
在这个领域中,我最赞赏的一件事就是对开源的接纳。即使是那些技术大佬们也乐于与大家分享新的突破和创新,从而使这些技术不再“曲高和寡”。
其中一项技术就是人脸识别,它可以解决很多现实问题(如果被正确又合乎伦理地使用)。本文将展示如何使用开源工具完成一个人脸识别的算法。以下是的一个有趣的演示,这也为接下来的内容做好铺垫:




所以,你准备好了吗?精彩才刚刚开始!
提示:如果你想要了解计算机视觉的复杂性, 下面这个深度学习的计算机视觉是很好的起步课程。 Computer Vision using Deep Learning https://trainings.analyticsvidhya.com/courses/course-v1:AnalyticsVidhya+CVDL101+CVDL101_T1 /about

内容
人脸识别中有前景的应用 系统准备—— 硬件/软件要求 硬件安装 软件安装 探究Python的实现 简单演练 人脸识别案例

人脸识别中有前景的应用

我找到了一些经典的人脸识别技术应用案例。你一定碰到过类似的例子,但并没有意识到这些场景背后到底使用了什么技术!
例如,对于每一张上传到平台的图像,Facebook都用自动生成的标签建议替代手动给图像加标签。Facebook使用了一个简单的人脸识别算法来分析图像中人脸的像素,同时将它和相关用户做比较。我们将学习如何创建一个人脸识别模型,但是在我们描述相关的技术细节之前,先来探讨一些其它的应用案例。
我们现在常常用最新的“人脸解锁”功能来解锁手机。这是一个很小的人脸识别技术案例,以帮助维护个人数据安全。同样的想法可以应用于更大范围,使相机能够在抓取图像的同时识别人脸。




人脸识别技术还应用于广告、医疗、银行等行业中,只是不太被人所知。在大多数公司,甚至在很多会议中,进入时都需要佩戴身份识别卡。但是我们能否找到一种无需佩戴任何身份识别卡就能进出的方法呢?人脸识别非常有助于这个想法的实现。人们只需要看着镜头,系统就会自动判断他是否具备权限。
另一个有趣的人脸识别应用是计算参与活动(如会议或音乐会)的人数。这并不是手动计算参加者的数量,我们可以安装一个摄像机,它能够捕捉参加者影像并计算人员总数。如此可以使得过程自动化,同时也能节省大量人力。这个技术非常实用,不是吗?





你可以想出更多类似的应用 ——在下面的留言中和我们分享吧!
本文将侧重于人脸识别的实践应用,对算法如何运作只作注释。如 果你想了解更多,可以浏览下面这篇文章:
Understanding and Building an Object Detection Model from Scratch in Python https://www.analyticsvidhya.com/blog/2018/06/understanding-building-object-detection-model-python/

系统准备 —— 硬件/软件要求

现在你知道了人脸识别技术可以实现的应用,让我们看看如何能够通过可用的开源工具来实现它。这就能体现领域的优势了 —— 分享开源代码的意愿和行动都是其他领域无法比拟的。
针对本文,以下列出了我使用的和推荐使用的系统配置如下: 一个网络摄像头(罗技 Logitech C920)用来搭建一个实时人脸识别器,并将其安装在联想E470 ThinkPad系列笔记本(英特尔酷睿5第7代内核Core i5 7th Gen)。你也可以使用笔记本电脑自带的摄像头,或电视监控系统摄像头。在任意系统上做实时的视频分析都可以,并不一定是我正在使用的配置。 使用图形处理器(GPU)来加速视频处理会更好。 在软件方面,我们使用Ubuntu 18.04操作系统安装所有必要的软件。

为了确保搭建模型前的每件事都安排妥当,接下来我们将详细探讨如何配置。
步骤一:硬件安装
首要的事就是检查网络摄像头是否正确安装。在Ubuntu上有一个简单小技巧 —— 看一下相应的设备在操作系统中是否已经被注册。你可以照着以下的步骤来做:
步骤1.1: 在连接网络摄像头到笔记本电脑之前,通过在命令提示符窗口输入命令ls /dev/video* 来检查所有连接上的视频设备。这将列出那些已经连接入系统的视频设备。




步骤1.2: 连接网络摄像头并再次执行这一命令。





如果网络摄像头被成功连接,将显示一个新的设备。
步骤1.3: 另一件你可以做的事是使用任一网络摄像头软件来检查摄像头能否正常工作,Ubuntu中你可以用“Cheese”来检查。



这里我们可以看出网络摄像头已经安装成功啦!硬件部分完成!
步骤二:软件安装
步骤2.1: 安装Python
本文中的代码是用Python 3.5编写。尽管有多种方法来安装Python,我还是建议使用Anaconda —— 数据科学中最受欢迎的Python发行版。 Anaconda下载链接: https://www.anaconda.com/download/

步骤2.2: 安装OpenCV
OpenCV(Open Source Computer Vision) 是一个旨在创建计算机视觉应用的软件库。它包含大量预先写好的图像处理功能。要安装OpenCV,需要一个该软件库的pip安装: pip3 install opencv-python
步骤2.3: 安装face_recognition应用程序接口
最后,我们将会使用face_recognition,它号称是世界上最简单的人脸识别应用程序Python接口。安装如下: pip install dlib pip install face_recognition
让我们开始探索它的实现吧。
既然你已经配置好了系统,是时候探究真正的使用了。首先,我们将快速地创建程序,然后分段来解释我们做了什么。

简单流程

首先,创建一个face_detector.py文件同时复制下面的代码: # import librariesimport cv2import face_recognition# Get a reference to webcam video_capture = cv2.VideoCapture("/dev/video1")# Initialize variablesface_locations = []while True: # Grab a single frame of video ret, frame = video_capture.read() # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses) rgb_frame = frame[:, :, ::-1] # Find all the faces in the current frame of video face_locations = face_recognition.face_locations(rgb_frame) # Display the results for top, right, bottom, left in face_locations: # Draw a box around the face cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) # Display the resulting image cv2.imshow('Video', frame) # Hit 'q' on the keyboard to quit! if cv2.waitKey(1) & 0xFF == ord('q'): break# Release handle to the webcamvideo_capture.release()cv2.destroyAllWindows()
然后,用如下命令执行这个Python文件: python face_detector.py
如果一切运行正确,会弹出一个新窗口,以运行实时的人脸识别。




总结一下,以上的代码做了这些: 首先,我们配置了运行影像分析的硬件。 接下来我们逐帧地捕捉实时影像。 然后我们处理每一帧并且提取图像中所有人脸的位置。 最后,我们以视频形式描绘出这些帧,同时标注人脸的位置。

这很简单,不是吗? 如果你想深入了解更多细节,我已经对各代码段做了详尽注解。你可以随时回顾我们做了什么。

人脸识别案例

有趣的事情并没有结束!我们还能做一件很酷的事情 —— 结合以上代码实现一个完整的案例。并且不需要从零开始,我们仅仅需要对代码做一些小的改动。
例如,你想搭建一个自动的摄像头定位系统,来实时跟踪演讲者的位置。根据他的位置,系统会转动摄像头使得演讲者总是处于视频的中间。
我们怎么做到这一点?第一步就是要搭建一个可辨识视频中人(们)的系统,并且重点放在演讲者的位置。




让我们来看一下如何才能实现这个案例。本文将以一个Youtube上的影片为例,视频记录了一个演讲者在2017年数据黑客峰会(DataHack Summit 2017)上的演讲。
首先,我们引入必要的代码库: import cv2 import face_recognition
然后,读入影片并得到影片长度: input_movie = cv2.VideoCapture("sample_video.mp4") length = int(input_movie.get(cv2.CAP_PROP_FRAME_COUNT))
随后我们新建一个输出文件,使其拥有和输入文件相似的分辨率和帧频。
载入演讲者的一个影像样本,来识别视频中的主角: image = face_recognition.load_image_file("sample_image.jpeg") face_encoding = face_recognition.face_encodings(image)[0] known_faces = [ face_encoding, ]
做完这些,现在我们可以执行一个循环来完成如下步骤: 从影片中提取一帧影像 找到所有的人脸并完成识别 新建一个影片,来将源帧图像和标注演讲者脸部的位置合并起来

让我们看看这部分代码: # Initialize variablesface_locations = []face_encodings = []face_names = []frame_number = 0while True: # Grab a single frame of video ret, frame = input_movie.read() frame_number += 1 # Quit when the input video file ends if not ret: break # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses) rgb_frame = frame[:, :, ::-1] # Find all the faces and face encodings in the current frame of video face_locations = face_recognition.face_locations(rgb_frame, model="cnn") face_encodings = face_recognition.face_encodings(rgb_frame, face_locations) face_names = [] for face_encoding in face_encodings: # See if the face is a match for the known face(s) match = face_recognition.compare_faces(known_faces, face_encoding, tolerance=0.50) name = None if match[0]: name = "Phani Srikant" face_names.append(name) # Label the results for (top, right, bottom, left), name in zip(face_locations, face_names): if not name: continue # Draw a box around the face cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) # Draw a label with a name below the face cv2.rectangle(frame, (left, bottom - 25), (right, bottom), (0, 0, 255), cv2.FILLED) font = cv2.FONT_HERSHEY_DUPLEX cv2.putText(frame, name, (left + 6, bottom - 6), font, 0.5, (255, 255, 255), 1) # Write the resulting image to the output video file print("Writing frame {} / {}".format(frame_number, length)) output_movie.write(frame)# All done!input_movie.release()cv2.destroyAllWindows()
这段代码将输出如下类似的结果:




人脸识别真的是一件了不起的事情!

总结

恭喜!你现在已经掌握了如何为一些现实场景搭建人脸识别系统。深度学习是一个如此令人着迷的领域,而我非常期待看到它未来的发展。
在这篇文章中,我们学习了如何在真实情境中运用开源工具来构建实时人脸识别系统。我希望你能创造更多类似的应用,并且自己尝试更多的案例。相信我,还有很多东西要学习,而他们真的很趣!
一如既往,如果你有任何问题或建议尽管在下面的留言部分提出来吧!

原文标题:
Building a Face Detection Model from Video using Deep Learning (Python Implementation)
原文链接:
https://www.analyticsvidhya.com/blog/2018/12/introduction-face-detection-video-deep-learning-python/
人工智能
2019-02-21 20:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
FP-Growth算法是韩嘉炜等人在2000年提出的关联分析算法,它采取如下分治策略:将提供频繁项集的数据库压缩到一棵频繁模式树(FP-tree),但仍保留项集关联信息。
在算法中使用了一种称为频繁模式树(Frequent Pattern Tree)的数据结构。FP-tree是一种特殊的前缀树,由频繁项头表和项前缀树构成。FP-Growth算法基于以上的结构加快整个挖掘过程。
众所周知,Apriori算法在产生频繁模式完全集前需要对数据库进行多次扫描,同时产生大量的候选频繁集,这就使Apriori算法时间和空间复杂度较大。但是Apriori算法中有一个很重要的性质:频繁项集的所有非空子集都必须也是频繁的。但是Apriori算法在挖掘额长频繁模式的时候性能往往低下,Jiawei Han提出了FP-Growth算法。
FP-Tree :将事务数据表中的各个事务数据项按照支持度排序后,把每个事务中的数据项按降序依次插入到一棵以 NULL为根结点的树中,同时在每个结点处记录该结点出现的支持度。
1、FPGrowth使用场景
FPGrowth关联规则算法主要用于发现频繁项集。如:沃尔玛啤酒加尿布。
2、FPGrowth基本概念
FPGrowth算法通过构造一个FPTree树结构来压缩数据记录,使得挖掘频繁项集只需要扫描两次数据记录,而且该算法不需要生成候选集合,所以效率会比较高。
那么,如何从购物篮里面发现 尿布+啤酒 这样的最佳组合呢?
我们以以下数据集为例,假设有如下的一张购物清单表,每条记录代表一次购物记录: |TID|Items| |-|-| | T1 | { 面包 , 牛奶 } | | T2 | { 面包 , 尿布 , 啤酒 , 鸡蛋 }| | T3 | { 牛奶 , 尿布 , 啤酒 , 可乐 }| | T4 | { 面包 , 牛奶 , 尿布 , 啤酒 }| | T5 | { 面包 , 牛奶 , 尿布 , 可乐 }|
其中: 牛奶、面包叫做 项 ; { 牛奶、面包}叫做 项集 ; 项集出现的次数叫做 支持度 。 T * 表示用户每次的购物清单。
3、算法思想
该算法的核心就是生成一棵FPTree。前面提到过,FPTree是一种树结构。
构建的过程需要将表中的数据以及关系进行保存,我们先来看构建过程:
假设我们的最小支持度是3,这相当于是一个阈值。接下来我们开始按如下的步骤处理数据。
3.1、step1
扫描数据记录,生成一级频繁项集,并按出现次数由多到少排序,如下所示: Item Count 牛奶 4 面包 4 尿布 4 啤酒 3 可乐 2(<3,删除) 鸡蛋 1(<3,删除)
可以看到,鸡蛋和可乐在上表中要删除,因为可乐只出现2次,鸡蛋只出现1次,小于最小支持度,因此不是 频繁项集 ,非频繁项集的超集一定不是频繁项集,所以可乐和鸡蛋不需要再考虑。
3.2、step2
再次扫描数据记录,对每条记录中出现在Step 1产生的表中的项,按表中的顺序排序。初始时,新建一个根结点,标记为null。然后依次扫描每条记录,构建FPTree。
1、第一条记录:{面包,牛奶}需要根据Step1中结果转换成:{牛奶,面包},新建一个结点,name为{牛奶},将其插入到根节点下,并设置count为1,然后新建一个{面包}结点,插入到{牛奶}结点下面,插入后如下所示:
2、第二条记录:{面包,尿布,啤酒,鸡蛋},过滤并排序后为:{面包,尿布,啤酒},发现根结点没有包含{面包}的儿子(有一个{面包}孙子但不是儿子),因此新建一个{面包}结点,插在根结点下面,这样根结点就有了两个孩子,随后新建{尿布}结点插在{面包}结点下面,新建{啤酒}结点插在{尿布}下面,插入后如下所示:
3、第三条记录:{牛奶,尿布,啤酒,可乐},过滤并排序后为:{牛奶,尿布,啤酒},这时候发现根结点有儿子{牛奶},因此不需要新建结点,只需将原来的{牛奶}结点的count加1即可,往下发现{牛奶}结点有一个儿子{尿布},于是新建{尿布}结点,并插入到{牛奶}结点下面,随后新建{啤酒}结点插入到{尿布}结点后面。插入后如下图所示:
4、第四条记录:{面包,牛奶,尿布,啤酒},过滤并排序后为:{牛奶,面包,尿布,啤酒},这时候发现根结点有儿子{牛奶},因此不需要新建结点,只需将原来的{牛奶}结点的count加1即可,往下发现{牛奶}结点有一个儿子{面包},于是也不需要新建{面包}结点,只需将原来{面包}结点的count加1,由于这个{面包}结点没有儿子,此时需新建{尿布}结点,插在{面包}结点下面,随后新建{啤酒}结点,插在{尿布}结点下面,插入后如下图所示:
5、按照1-4这样的方式迭代所有的记录,最终会生成一棵树,即FPTree。按上表中生成的最终的FPTree如下图所示:
树中每个路径代表一个项集,因为许多项集有公共项,而且出现次数越多的项越可能是公共项,因此按出现次数由多到少的顺序排序可以节省空间,实现压缩存储。
另外我们需要一个表头和对每一个name相同的结点做一个线索,方便后面使用,线索的构造也是在建树过程形成的(下图虚线)。
至此,整个FpTree就构造好了。
4、利用FPTree挖掘频繁项集
FPTree建好后,就可以进行频繁项集的挖掘,挖掘算法称为FPGrowth(Frequent Pattern Growth)算法,挖掘从表头header的最后一个项开始。
此处即从{啤酒}开始,根据{啤酒}的线索链找到所有{啤酒}结点,然后找出每个{啤酒}结点的分支:{牛奶,面包,尿布,啤酒:1},{牛奶,尿布,啤酒:1},{面包,尿布,啤酒:1},其中的“1”表示出现1次。
注意,虽然{牛奶}出现4次,但{牛奶,面包,尿布,啤酒}只同时出现1次,因此 分支的count是由后缀结点{啤酒}的count决定的 ,除去{啤酒},我们得到对应的前缀路径{牛奶,面包,尿布:1},{牛奶,尿布:1},{面包,尿布:1},根据前缀路径我们可以生成一棵条件FPTree,构造方式跟之前一样,此处的数据记录变为: T1 {牛奶,面包,尿布 : 1} T2 {牛奶,尿布: 1} T3 {面包,尿布: 1}
绝对支持度依然是3,我们发现此时,牛奶的支持度为2、面包的支持度为2、尿布的支持度为3,由于我们的支持度为3,所以删除牛奶和面包。按照相同的算法构造得到的FPTree为:
构造好条件树后,对条件树进行递归挖掘,当条件树只有一条路径时,路径的所有组合即为条件频繁集,假设{啤酒}的条件频繁集为{S1,S2},则{啤酒}的频繁集为{S1+{啤酒},S2+{啤酒},S1+S2+{啤酒}},即{啤酒}的频繁集一定有相同的后缀{啤酒},此处的条件频繁集为:{{},{尿布}},于是{啤酒}的频繁集为{{啤酒}{尿布,啤酒}}。
接下来找header表头的倒数第二个项{尿布}的频繁集,同上可以得到{尿布}的前缀路径为:{面包:1},{牛奶:1},{牛奶,面包:2},条件FPTree的数据集为: T1 {面包 :1 } T2 {牛奶 :1} T3 {牛奶,面包 :2}
构造的条件FpTree为:
这颗条件树路径上的所有组合即为条件频繁集: {{},{牛奶},{面包},{牛奶,面包}}
加上{尿布}后,又得到一组频繁项集 {{尿布},{牛奶,尿布},{面包,尿布},{牛奶,面包,尿布}}
同样,这组频繁项集一定包含一个相同的后缀:{尿布},也就是我们一开始分析的对象,并且不包含{啤酒},因此这一组频繁项集与上一组不会重复。
重复以上步骤,对header表头的每个项进行挖掘,即可得到整个频繁项集,频繁项集即不重复也不遗漏。
最终,我们可以得到多个集合列表,每一个集合代表一个频繁项集。
总结下来,FPTree的开发流程为2个:
1、生成树过程
2、挖掘树过程:挖掘树的过程也是一个生成树的过程,每次挖掘一个节点的目的,都是为了发现该节点的频繁项集,将最终生成的结果取所有子集然后将每一项与挖掘的节点组合,作为我们最后得到的结果。
Spark Mlib实现
训练集如下: 面包 牛奶 面包 尿布 啤酒 鸡蛋 牛奶 尿布 啤酒 可乐 面包 牛奶 尿布 啤酒 面包 牛奶 尿布 可乐
存储在路径 data/sample_fpgrowth.txt 中。
接下来调用FPGrouwth算法训练数据集。 package com.zhaoyi;// $example on$ import java.util.Arrays; import java.util.List; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.apache.spark.mllib.fpm.AssociationRules; import org.apache.spark.mllib.fpm.FPGrowth; import org.apache.spark.mllib.fpm.FPGrowthModel; // $example off$ import org.apache.spark.SparkConf; public class JavaSimpleFPGrowth { public static void main(String[] args) { SparkConf conf = new SparkConf().setMaster("local[*]").set("spark.testing.memory","2140000000") .setAppName("JavaLinearRegressionWithSGDExample"); JavaSparkContext sc = new JavaSparkContext(conf); // $example on$ JavaRDD data = sc.textFile("data/sample_fpgrowth.txt"); JavaRDD> transactions = data.map(line -> Arrays.asList(line.split(" "))); // 最小支持度为0.5 FPGrowth fpg = new FPGrowth() .setMinSupport(0.5) .setNumPartitions(10); FPGrowthModel model = fpg.run(transactions); for (FPGrowth.FreqItemset itemset: model.freqItemsets().toJavaRDD().collect()) { System.out.println("[" + itemset.javaItems() + "], " + itemset.freq()); } double minConfidence = 0.8; for (AssociationRules.Rule rule : model.generateAssociationRules(minConfidence).toJavaRDD().collect()) { System.out.println( rule.javaAntecedent() + " => " + rule.javaConsequent() + ", " + rule.confidence()); } // $example off$ sc.stop(); } } 面包 牛奶 面包 尿布 啤酒 鸡蛋 牛奶 尿布 啤酒 可乐 面包 牛奶 尿布 啤酒 面包 牛奶 尿布 可乐
可以看到,我们设置的最小支持度为0.5,也就是说,过滤过程中,会将低于出现次数小于3/5>0.5>2/5次的话,显然可乐(出现一次,支持度为2/5=0.4)以及鸡蛋(出现1次,支持度为1/5=0.2)都不会纳入频繁项集之中,在step1中就会被T除。
最终的输出结果 [[尿布]], 4 [[牛奶]], 4 [[牛奶, 尿布]], 3 [[面包]], 4 [[面包, 牛奶]], 3 [[面包, 尿布]], 3 [[啤酒]], 3 [[啤酒, 尿布]], 3 [啤酒] => [尿布], 1.0
可以看到,该输出结果完美的实现了牛奶尿布实例中的预测结果。即{啤酒,尿布}这样的组合频繁项集。
那么,我们可以考虑将这两种商品摆放到一起,从而起到一定的增加销售业绩的作用(事实上沃尔玛的确这么做了)。
人工智能
2019-02-21 20:05:01
「深度学习福利」大神带你进阶工程师,立即查看>>>
“ Artificial-Intelligently Challenged ”
前言
大家好,我又出来怼人了。
两年前,写了一篇文章 《为什么现在的人工智能助理都像人工智障》 ,当时主要是怼“智能助理们”。这次呢则是表达 “我不是针对谁,只是现在所有的深度学习都搞不定对话AI”,以及“你看都这样了, 那该怎么做 AI 产品 ”。
- 阅读门槛 - 时间: 这篇真的太长了(近3万字) 根据预览同学们的反馈,通常第一次阅读到Part 3时,会消耗很多精力,但读完Part 3才发现是精华(同时也是最烧脑的部分)。请大家酌情安排阅读时间。 可读性: 我会在内容里邀请你一起思考(无需专业知识),所以可能不适合通勤时间阅读。你的阅读收益取决于在过程中思考的参与程度。 适合人群 :对话智能行业从业者、AIPM、关注AI的投资人、对AI有强烈兴趣的朋友、关心自己的工作会不会被AI代替的朋友; 关于链接 :阅读本文时,无需阅读每个链接里的内容,这并不会影响对本文的理解。
- 关于 “ 人工智障 ” 四个字 -
上一片文章发出后,有朋友跟我说,标题里的“人工智障”这个词貌似有点offensive。作为学语言出身的,我来解释一下这个原因:
最开始呢,我是在跟一位企业咨询顾问聊人工智能这个赛道的现状。因为对话是用英语展开的,当时为了表达我的看法 “现在的智能助理行业正处在一种难以逾越的困境当中”,我就跟她说“Currently all the digital assistants are Artificial-Intelligently challenged”。
她听了之后哈哈一笑。“intelligently challenged”同时也是英文中对智障的委婉表达。 假设不了解这个常识,她就可能忽略掉这个梗,尽管能明白核心意思,只是不会觉得有什么好笑的。那么信息在传递中就有损失。
写文章时,我把这个信息翻译成中文,就成了“人工智障”。但是因为中文语法的特性,有些信息就lost in translation了。比如实际表达的是“一种困境的状态”而不是“一件事”。
(顺便说一下,中文的智障,实际上是政治正确的称呼,详见 特殊奥运会的用词方法 。)
为什么要写那么多字来解释这个措辞?因为 不同的人,看见相同的字,也会得到不同的理解。 这也是我们要讨论的重点之一。
那么,我们开始吧。
Part 1
对话智能的表现:智障
Sophia in AI for Good Global Summit 2017. Source: ITU
2017年10月,上图这个叫Sophia的机器人, 被沙特阿拉伯授予了正式的公民身份 。公民身份,这个评价比图灵测试还要牛。何况还是在沙特,他们才刚刚允许女性开车不久( 2017年9月颁布的法令 )。
Sophia经常参加各种会、“发表演讲”、“接受采访”,比如 去联合国对话 ,表现出来非常类似人类的言谈;去 和Will Smith拍MV ;接受 Good morning Britain 之类的主流媒体的采访;甚至公司创始人 参加Jim Fallon的访谈时一本正经的说Sophia是“basically alive” 。
Basically alive. 要知道,西方的吃瓜群众都是看着《终结者》长大的,前段时间还看了《西部世界》。在他们的世界模型里,“机器智能会觉醒” 这个设定是迟早都会发生的。
普通大众开始吓得瑟瑟发抖。不仅开始担心自己的工作是不是会被替代,还有很多人开始担心AI会不会统治人类,这样的话题展开。“未来已来”,很多人都以为真正的人工智能已经近在咫尺了。
只是,有些人可能会注意到有些不合理的地方:“等等,人工智能都要威胁人类了,为啥我的Siri还那么蠢?”
Source: Dumb And Dumber: Comparing Alexa, Siri, Cortana And The Google Assistant, Forbes, May 2018
我们来看看到2018年末在对话智能领域,各方面究竟发展的如何了。
“ 不要日本菜 ”
我在2016年底做过一个测试,对几个智能助理提一个看似简单的需求:“推荐餐厅,不要日本菜”。只是各家的AI助理都会给出一堆餐厅推荐,全是日本菜。
2年过去了,在这个问题的处理上有进展么?我们又做了一次测试:
结果是依然没有解决。“不要”两个字被所有助理一致忽略了。
为什么要关注“不要”两个字?之前我去到一家某非常有名的智能语音创业公司,聊到这个问题时,他家的PM显出疑惑:“这个逻辑处理有什么用?我们后台上看到用户很少提出这类表达啊。”
听到这样的评论,基本可以确定:这家公司还没有深入到专业服务对话领域。
场景方面,一旦深入进服务领域里的多轮对话,很容易会遇到类似这样的表达 :“我不要这个,有更便宜的么?”。后台没有遇到,只能说用户还没开始服务就结束了。场景方面与AI公司的domain选择有关。
但是在技术方面,则是非常重要的。因为这正是真正智能的核心特点。我们将在part 2&3详细聊聊这个问题。现在先抛个结论:这个问题解决不了,智能助理会一直智障下去的。
“ To C 团队转 To B ”
自从2015年几个重要的深度学习在开发者当中火了起来,大小公司都想做“Her”这样面对个人消费者的通用型智能助理(To C类产品的终极目标)。一波热钱投给最有希望的种子队伍(拥有Fancy背景)之后,全灭。目前为止,在2C这方面的所有商用产品,无论是巨头还是创业公司,全部达不到用户预期。
在人们的直觉里,会认为“智能助理”,处理的是一些日常任务,不涉及专业的需求,应该比“智能专家”好做。这是延续“人”的思路。推荐餐厅、安排行程是人人都会做的事情;却只有少数受过专业训练的人能够处理金融、医疗问诊这类专业问题。
而对于现在的AI,情况正好相反。现在能造出在围棋上打败柯洁的AI,但是却造不出来能给柯洁管理日常生活的AI。
随着to C助理赛道的崩盘,To B or not to B已经不再是问题,因为已经没得选了,只能To B。这不是商业模式上的选择,而是技术的限制。目前To B,特别是限定领域的产品,相对To C类产品更可行:一个原因是领域比较封闭,用户从思想到语言,不容易发挥跑题;另一方面则是数据充分。
只是 To B的公司都很容易被当成是做“外包”的 。因为客户是一个个谈下来的,项目是一个个交付的,这意味着增长慢,靠人堆,没有复利带来的指数级增长。大家纷纷表示不开心。
这个“帮人造机器人”的业务有点像“在网页时代帮人建站”。转成To B的团队经常受到资本的质疑: “你这个属于做项目,怎么规模化呢?”
要知道,国内的很多投资机构和里面的投资经理入行的时间,是在国内的移动互联起来的那一波。“Scalability”或者“高速增长”是体系里最重要的指标,没有之一。而做项目这件事,就是Case by case,要增长就要堆人,也就很难出现指数级增长。这就有点尴尬了。
“你放心,我有SaaS!哦不,是AIaaS。我可以打造一个平台,上面有一系列工具,可以让客户们自己组装机器人。”
然而,这些想做技能平台的创业公司,也没有一个成功的。短期也不可能成功。
Yann LeCun对AIaas的看法
主要的逻辑是这样的:你给客户提供工具,但他需要的是雕像——这中间还差了一个雕塑家。佐证就是那些各家试图开放“对话框架”给更小的开发者,甚至是服务提供者,帮助他们“3分钟开发出自己的AI机器人”,具体就不点名了。自己都开发不出来一个让人满意的产品,还想抽象一个范式出来让别人沿用你的(不work的)框架?
不过,我认为MLaaS在长期的成功是有可能的,但还需要行业发展更为成熟的时候,现在为时尚早。具体分析我们在后面Part 5会谈到。
“ 音箱的成功和智能的失败 ”
对话这个领域,另一个比较火的赛道是智能音箱。
各大主要科技公司都出了自己的智能音箱,腾讯叮当、阿里的天猫精灵、小米音箱、国外的Alexa、Google的音箱等等。作为一个硬件品类,这其实是个还不错的生意,基本属于制造业。
不仅出货不差,还被寄予期望,能够成为一个生态的生意——核心逻辑看上去也是充满想象力的: 超级终端:在后移动时代,每家都想像iphone一样抢用户的入口。只要用户习惯使用语音来获得咨询或者服务,甚至可以像Xbox/ps一样,硬件赔钱卖,软件来挣钱; 用语音做OS:开发者打造各类语音的技能,然后通过大量“离不开的技能” 反哺这个OS的市场占有; 提供开发者平台:像Xcode一样,给开发者提供应用开发的工具和分发平台、提供使用服务的流量。
可是,这些技能使用的实际情况是这样的:
Source: Statista 万众期待的killer app并没有出现; 基本没有商业服务型的应用; 技能开发者都没赚到钱,也不知道怎么赚钱; 大部分高频使用的技能都没有商业价值——用户用的最多的就是“查天气” 没有差异性:智能的差异嘛基本都没有的事儿。
“ 皇帝的新人工智能 ”
回过头来,我们再来看刚刚那位沙特阿拉伯的公民,Sophia。既然刚刚提到的那么多公司投入了那么多钱和科学家,都搞成这样,凭什么这个Sophia能一鸣惊人?
因为Sophia的“智能” 是个骗局。
可以直接引用Yann LeCun对此的评价, “这完全是鬼扯”。
简单来说,Sophia是一个带喇叭的木偶——在各种大会上的发言和采访的内容都是人工撰写,然后用人人都有的语音合成做输出。却被宣传成为是其“人工智能”的自主意识言论。
这还能拿“公民身份”,可能是人类公民被黑的最惨的一次。这感觉,好像是我家的橘猫被一所985大学授予了土木工程学士学位。
其实对话系统里,用人工来撰写内容,或者使用模版回复,这本来就是现在技术的现状(在后面我们会展开)。
但刻意把“非智能”的产物说成是“智能”的表现,这就不对了。
考虑到大部分吃瓜群众是通过媒体渠道来了解当前技术发展的,跟着炒作的媒体(比如被点名的Tech Insider)都是这场骗局的共犯。这些不知道是无知还是无良的文科生,真的没有做好新闻工作者份内的调查工作。
最近这股妖风也吹到了国内的韭菜园里。
Sophia出现在了王力宏的一首讲AI的MV里;然后又2018年11月跑去给大企业站台。
真的,行业内认真做事儿的小伙伴,都应该站出来,让大家更清晰的知道现在AI——或者说机器学习的边界在哪儿。不然甲方爸爸们信以为真了,突然指着sophia跟你说,“ 别人都能这么自然,你也给我整一个。”
你怕不得装个真人进去?
对了,说到这儿,确实现在也有:用人——来伪装成人工智能——来模拟人,为用户服务。
Source: The Guardian
国内的案例典型的就是银行用的大堂机器人,其实是真人在远程语音(所谓Tele presence)。美国有X.ai,做基于Email的日程管理的。只是这个AI到了下午5点就要下班。
当然,假如我是这些骗局背后开发者,被质疑的时候,我还可以强行拉回人工智能上:“这么做是为了积累真正的对话数据,以后用来做真的AI对话系统识别的训练。”
这么说对外行可能是毫无破绽的。但是真正行业内干正经事的人,都应该像傅盛那样站出来, 指明这些做法是骗人:“全世界没有一家能做出来......做不到,一定做不到” 。
人家沙特是把AI当成人,这些套路是把人当成AI。然后大众就开始分不清楚究竟什么是AI了。
“ 人工智能究竟( tmd )指的是什么? ”
另一方面,既然AI现在的那么蠢,为什么马一龙 (Elon Musk) 却说 “AI很有可能毁灭人类” ;霍金甚至直接说 “AI可能是人类文明里最糟糕的事件” 。
而在另一边,Facebook和Google的首席科学家却在说,现在的AI都是渣渣, 根本不需要担心 ,甚至应该推翻重做。 大家该相信谁的?一边是要去火星的男人,和说不定已经去了火星的男人;另一边是当前两家科技巨头的领军人物。
其实他们说的都对,因为这里说到的“人工智能”是两码事。 马一龙和霍金担心的人工智能,是由人造出来的真正的智能,即通用人工智能(AGI, Artificial General Intelligence)甚至是超级智能(Super Intelligence)。
而Yann LeCun 和Hinton指的人工智能则是指的当前用来实现“人工智能效果”的技术(基于统计的机器学习)。这两位的观点是“用这种方式来实现人工智能是行不通的”。
两者本质是完全不同的,一个指的是结果,一个指的是(现在的)过程。
那么当我们在讨论人工智能的时候,究竟在说什么?
John McCathy
John McCathy在1956年和Marvin Minsky,Nathaniel Rochester 以及Claude Shannon在达特貌似研讨会上打造了AI这个词,但是到目前为止,学界工业界并没有一个统一的理解。
最根本的问题是目前人类对“智能”的定义还不够清楚。何况人类本身是否是智能的最佳体现,还不一定呢。想想每天打交道的一些人:)
一方面,在大众眼中,人工智能是 “人造出来的,像人的智能”,比如Siri。同时,一个AI的水平高低,则取决于它有多像人。所以当Sophia出现在公众眼中的时候,普通人会很容易被蒙蔽(甚至能通过图灵测试)。
Oracle对AI的定义也是 “只要是能让计算机可以 模拟 人类行为的技术,都算!”
而另一方面,从字面上来看“Artificial Intelligence”,只要是人造的智能产品,理论上都算作人工智能。
也就是说,一个手持计算器,尽管不像人,也应算是人工智能产品。但我相信大多数人都不会把计算器当成是他们所理解的人工智能。
这些在认识上不同的解读,导致当前大家对AI应用的期望和评估都有很多差异。
再加上还有“深度学习、神经网络、机器学习” 这些概念纷纷跟着人工智能一起出现。但是各自意味着什么,之间是什么关系,普通大众都不甚了解。
“ 没关系,韭菜不用懂。” 但是想要割韭菜的人,最好能搞清楚吧。连有些投资人自己也分不清,你说怎么做判断,如何投项目?当然是投胸大的。
以上,就是到2018年末,在对话领域的人工智能的现状:智能助理依然智障;大部分To B的给人造机器人的都无法规模化;对话方面没有像AlphaZero在围棋领域那样的让人震惊的产品;没有商业上大规模崛起的迹象;有的是一团浑水,和浑水摸鱼的人。
为什么会这样?为什么人工智能在图像识别,人脸识别,下围棋这些方面都那么快的进展,而在对话智能这个领域却是如此混乱?
既然你都看到这里了,我相信你是一个愿意探究本质的好同志。那么我们来了解,对话的本质是什么;以及现在的对话系统的本质又是什么。
Part 2
当前对话系统的本质:填表
“ AI thinks, man laughs ”
Source:The Globe and Mail
有一群小鸡出生在一个农场,无忧无虑安心地生活。
鸡群中出现了一位科学家,它注意到了一个现象:每天早上,食槽里会自动出现粮食。
作为一名优秀的归纳法信徒(Inductivist),这只科学鸡并不急于给出结论。它开始全面观察并做好记录,试图发现这个现象是否在不同的条件下都成立。
“星期一是这样,星期二是这样;树叶变绿时是这样,树叶变黄也是这样;天气冷是这样,天气热也是这样;下雨是这样,出太阳也是这样!”
每天的观察,让它越来越兴奋,在心中,它离真相越来越接近。直到有一天,这只科学鸡再也没有观察到新的环境变化,而到了当天早上,鸡舍的门一打开,它跑到食槽那里一看,依然有吃的!
科学鸡,对他的小伙伴,志在必得地宣布:“我预测,每天早上,槽里会自动出现食物。明天早上也会有!以后都会有!我们不用担心饿死了!”
经过好几天,小伙伴们都验证了这个预言,科学鸡骄傲的并兴奋的把它归纳成“早起的小鸡有食吃定理”。
正好,农场的农夫路过,看到一只兴奋的鸡不停的咯咯叫,他笑了:“这只鸡很可爱哦,不如把它做成叫花鸡好了” 。
科学鸡,卒于午饭时间。
在这个例子里,这只罗素鸡( Bertrand Russell ’s chicken)只对现象进行统计和归纳,不对原因进行推理。
而主流的基于统计的机器学习特别是深度学习,也是通过大量的案例,靠对文本的特征进行归类,来实现对识别语义的效果。这个做法,就是罗素鸡。
目前,这是对话式人工智能的主流技术基础。其主要应用方向,就是对话系统,或称为Agent。之前提到的智能助理Siri,Cortana,Google Assistant以及行业里面的智能客服这些都算是对话智能的应用。
“ 对话智能的黑箱 ”
这些产品的交互方式,是人类的自然语言,而不是图像化界面。
图形化界面(GUI)的产品,比如网页或者APP的产品设计,是所见即所得、界面即功能。
对话智能的交互(CUI, Conversational UI)是个黑箱:终端用户能感知到自己说出的话(输入)和机器人的回答(输出)——但是这个处理的过程是感觉不到的。就好像跟人说话,你并不知道他是怎么想的。
每一个对话系统的黑箱里,都是开发者自由发挥的天地。
虽说每家的黑箱里面都不同,但是最底层的思路,都万变不离其宗,核心就是两点: 听人话(识别) + 讲人话(对话管理) 。
如果你是从业人员,那么请回答一个问题:你们家的对话管理是不是填槽?若是,你可以跳过这一节(主要科普填槽是怎么回事),请直接到本章的第五节“ 当前对话系统的局限 ” 。
“ AI 如何听懂人话 ? ”
对话系统这个事情在2015年开始突然火起来了,主要是因为一个技术的普及:机器学习特别是深度学习带来的语音识别和NLU(自然语言理解)——主要解决的是识别人讲的话。
这个技术的普及让很多团队都掌握了一组关键技能:意图识别和实体提取。这意味着什么?我们来看一个例子。
在生活中,如果想要订机票,人们会有很多种自然的表达: “订机票”;
“有去上海的航班么?”;
“看看航班,下周二出发去纽约的”;
“要出差,帮我查下机票”;
等等等等
可以说“自然的表达” 有无穷多的组合(自然语言)都是在代表 “订机票” 这个意图的。而听到这些表达的人,可以准确理解这些表达指的是“订机票”这件事。
而要理解这么多种不同的表达,对机器是个挑战。在过去,机器只能处理“结构化的数据”(比如关键词),也就是说如果要听懂人在讲什么,必须要用户输入精确的指令。
所以,无论你说“我要出差”还是“帮我看看去北京的航班”,只要这些字里面没有包含提前设定好的关键词“订机票”,系统都无法处理。而且,只要出现了关键词,比如“我要退订机票”里也有这三个字,也会被处理成用户想要订机票。
自然语言理解这个技能出现后,可以让机器从各种自然语言的表达中,区分出来,哪些话归属于这个意图;而那些表达不是归于这一类的,而不再依赖那么死板的关键词。比如经过训练后,机器能够识别“帮我推荐一家附近的餐厅”,就不属于“订机票”这个意图的表达。
并且,通过训练,机器还能够在句子当中自动提取出来“上海”,这两个字指的是目的地这个概念(即实体);“下周二”指的是出发时间。
这样一来,看上去“机器就能听懂人话啦!”。
这个技术为啥会普及?主要是 因为机器学习领域的学术氛围,导致重要的论文基本都是公开的 。不同团队要做的是考虑具体工程实施的成本。
最后的效果,就是在识别自然语言这个领域里, 每家的基础工具都差不多 。在意图识别和实体提取的准确率,都是百分点的差异。既然这个工具本身不是核心竞争力,甚至你可以用别家的,大把可以选,但是关键是你能用它来干什么? “Due to the academic culture that ML comes from, pretty much all of the primary science is published as soon as it’s created - almost everything new is a paper that you can read and build with. But what do you build? ”
——Benedict Evans (A16Z合伙人)
在这方面,最显而易见的价值,就是解放双手。语音控制类的产品,只需要听懂用户的自然语言,就去执行这个操作:在家里要开灯,可以直接说 “开灯”,而不用去按开关;在车上,说要“开天窗”,天窗就打开了,而不用去找对应的按钮在哪里。
这类系统的重点在于,清楚听清哪个用户在讲是什么。所以麦克风阵列、近场远场的抗噪、声纹识别讲话的人的身份、ASR(语音转文字),等等硬件软件的技术就相应出现,向着前面这个目标不断优化。
“讲人话”在这类应用当中,并不那么重要。通常任务的执行,以结果进行反馈,比如灯应声就亮了。而语言上的反馈,只是一个辅助作用,可有可无。
但是任务类的对话智能,往往不止是语音控制这样一轮交互。如果一个用户说,“看看明天的机票”——这表达正常,但无法直接去执行。因为缺少执行的必要信息:1)从哪里出发?和 2)去哪里?
如果我们希望AI Agent来执行这个任务,一定要获得这两个信息。对于人来完成这个业务的话,要获得信息,就得靠问这个用户问题,来获得信息。很多时候,这样的问题,还不止一个,也就意味着,要发起多轮对话。
对于AI而言,也是一样的。 要知道 “去哪里” = Agent 问用户“你要去哪里?”
要知道 “从哪里出发” = Agent 问用户“你要从哪里出发呢?”
这就涉及到了对话语言的生成。
“ AI 如何讲人话? ”
决定“该说什么话”,才是对话系统的核心 ——无论是硅基的还是碳基的智能。但是深度学习在这个版块,并没有起到什么作用。
在当前,处理“该说什么”这个问题,主流的做法是由所谓“ 对话管理 ”系统决定的。
尽管每一个对话系统背后的“对话管理”机制都不同,每家都有各种理解、各种设计,但是万变不离其宗——目前所有任务类对话系统,无论是前段时间的Google duplex,还是智能客服,或者智能助理,最核心的对话管理方法,有且仅有一个:“填槽”,即Slot filling。
如果你并不懂技术,但是又要迅速知道一家做对话AI的水平如何,到底有没有黑科技(比如刚刚开始看AI领域的做投资的朋友 ),你只需要问他一个问题:“是不是填槽?” 如果他们(诚实地)回答“是”,那你就可以放下心来,黑科技尚未出现。接下来,能讨论的范围,无非都是产品设计、工程实现、如何解决体验和规模化的困境,这类的问题。基本上该智障的,还是会智障。 要是他们回答“不是填槽”,而且产品的效果还很好,那么就有意思了,值得研究,或者请速速联系我:)
那么这个“填槽”究竟是个什么鬼?嗯,不搞开发的大家可以简单的把它理解为“填表”:好比你要去银行办个业务,先要填一张表。
如果这张表上的空没有填完,柜台小姐姐就不给你办。她会红笔给你圈出来:“必须要填的空是这些,别的你都可以不管。” 你全部填好了,再递给小姐姐,她就去给你办理业务了。
还记得刚刚那个机票的例子么?用户说“看看明天的机票”,要想执行“查机票”,就得做以下的步奏,还要按顺序来:
1. ASR:把用户的语音,转化成文字。
2. NLU语义识别:识别上面的文字,属于(之前设定好的)哪一个意图,在这里就是“订机票”;然后,提取文字里面的实体,“明天”作为订票日期,被提取出来啦。
3. 填表:这个意图是订机票,那么就选“订机票”这张表来填;这表里有三个空,时间那个空里,就放进“明天”。
(这个时候,表里的3个必填项,还差两个:“出发地”和“到达地”)
4. 开始跑之前编好的程序:如果差“出发地”,就回“从哪里走啊?”;如果差“目的地”,就回“你要去哪里?”(NLG上打引号,是因为并不是真正意义上的自然语言生成,而是套用的对话模版)
5. TTS:把回复文本,合成为语音,播放出去
在上面这个过程当中,1和2步奏都是用深度学习来做识别。如果这个环节出现问题,后面就会连续出错。
循环1-5这个过程,只要表里还有空要填,就不断问用户,直到所有的必填项都被填完。于是,表就可以提交小姐姐(后端处理)了。
后端看了要查的条件,返回满足这些条件的机票情况。Agent再把查询结果用之前设计好的回复模板发回给用户。
顺便说一下,我们经常听到有些人说“我们的多轮对话可以支持xx轮,最多的时候有用户能说xx轮”。现在大家知道,在任务类对话系统里,“轮数的产生”是由填表的次数决定的,那么这种用“轮数多少”来衡量产品水平的方法,在这个任务类对话里里完全无意义。
一定要有意义,也应该是:在达到目的、且不影响体验的前提下,轮数越少越好。
在当前,只要做任务类的多轮对话,基本跑不掉填表。
5月的时候,Google I/O发布了Duplex的录音Demo,场景是Google Assistant代替用户打电话去订餐厅,和店员沟通,帮助用户预定位子。值得注意,这并不是Live demo。

Google's Assistant. CREDIT:GOOGLE
那Google的智能助理(后称IPA)又怎么知道用户的具体需求呢?跑不掉的是,用户还得给Google Assistant填一张表,用对话来交代自己的具体需求,比如下面这样:
图中左边是一个使用Google Assistant订餐厅的 真实案例,来自The Verge 。
“ 当前对话系统的局限 ”
我刚刚花了两千来个字来说明对话系统的通用思路。接下来,要指出这个做法的问题
还记得之前提到的 “不要日本菜”测试么?我们把这个测试套用在“订机票”这个场景上,试试看:“看看明天去北京的航班,东航以外的都可以”,还是按步奏来: 1. ASR语音转文字,没啥问题;
2. 语义识别,貌似有点问题
- 意图:是订机票,没错;
- 实体提取:跟着之前的训练来;
- 时间:明天
- 目的地:北京
- 出发地:这个用户没说,一会得问问他...
等等,他说的这个“东航以外的都可以”,指的是啥?之前没有训练过与航空公司相关的表达啊。
没关系,咱们可以把这个表达的训练加上去:东航 = 航司。多找些表达,只要用户说了各个航空公司的名字的,都训练成航司这个实体好啦。
另外,咱们还可以在填表的框里,添加一个航司选择,就像这样(黄色部分):
(嗯,好多做TO B的团队,都是掉在这个“在后面可以加上去”的坑里。)
但是,这么理所当然的训练之后,实体提取出来的航司却是“东航”——而用户说的是 “东航以外的”,这又指的哪个(些)航司呢?
“要不,咱们做点Trick把‘以外’这样的逻辑单独拿出来手工处理掉?”——如果这个问题可以这么容易处理掉,你觉得Siri等一干货色还会是现在这个样子? 难度不在于“以外”提取不出来,而是在处理“这个以外,是指哪个实体以外?
当前基于深度学习的NLU在“实体提取”这个技术上,就只能提取“实体”。
而人能够理解,在这个情况下,用户是指的“排除掉东航以外的其他选择”,这是因为人除了做“实体提取”以外,还根据所处语境,做了一个对逻辑的识别:“xx以外”。然后,自动执行了这个逻辑的处理,即推理,去进一步理解,对方真正指的是什么(即指代)。
而这个逻辑推理的过程,并不存在于之前设计好的步奏(从1到5)里。
更麻烦的是,逻辑的出现,不仅仅影响“实体”,还影响“意图”:
“hi Siri,别推荐餐厅”——它还是会给你推荐餐厅;
“hi Siri,除了推荐餐厅,你还能推荐什么?”——它还是会给你推荐餐厅。
中文英文都是一样的;Google assistant也是一样的。
想要处理这个问题,不仅仅是要识别出“逻辑”;还要正确判断出,这个逻辑是套用在哪个实体,或者是不是直接套用在某一个意图上。这个判断如何做?用什么做?都不在当前SLU的范围内。
对这些问题的处理,如果是集中在一些比较封闭的场景下,还可以解决个七七八八。但是,如果想要从根本上、泛化的处理, 希望一次处理就解决所有场景的问题,到目前都无解 。在这方面,Siri是这样,Google Assistant也是这样,任意一家,都是这样。
为啥说无解?我们来看看测试。
“ 用图灵测试来测对话系统没用 ”
一说到对人工智能进行测试,大部分人的第一反应是图灵测试。
5月Google I/O大会的那段时间,我们团队正在服务一家全球100强企业,为他们规划基于AI Agent的服务。
在发布会的第二天,我收到这家客户的Tech Office的好心提醒:Google这个像真人一样的黑科技,会不会颠覆现有的技术方案?我的回答是并不会。
话说Google Duplex在发布会上的demo确实让人印象深刻,而且大部分看了Demo的人,都分辨不出打电话去做预定的是不是真人。
“这个效果在某种意义上,算是通过了图灵测试。”
Google母公司的Chairman说google duplex可以算过了图灵测试了
由于 图灵测试的本质是“欺骗” (A game of deception, 详见Toby Walsh的论文 ),所以很多人批评它,这只能用来测试人有多好骗,而不是用来测智能的。在这一点上,我们在后文Part 4对话的本质中会有更多解释。
人们被这个Demo骗到的主要原因,是因为合成的语音非常像真人。
这确实是Duplex最牛的地方:语音合成。不得不承认,包括语气、音调等等模拟人声的效果,确实是让人叹为观止。只是,单就在语音合成方面,就算是做到极致,在 本质上就是一只鹦鹉——最多可以 骗骗Alexa (所以你看活体识别有多么重要)。
只是,Google演示的这个对话系统,一样处理不了逻辑推理、指代这类的问题。这意味着,就它算能过图灵测试,也过不了 Winograd Schema Challenge 测试。
相比图灵测试,这个 测试是直击深度学习的要害 。当人类对句子进行语法分析时,会用真实世界的知识来理解指代的对象。这个测试的目标,就是测试目前深度学习欠缺的常识推理能力。
如果我们用Winograd Schema Challenge的方法,来测试AI在“餐厅推荐”这个场景里的水平,题目会是类似这样的: A. “四川火锅比日料更好,因为它很辣”
B. “四川火锅比日料更好,因为它不辣”
AI需要能准确指出:在A句里,“它”指的是四川火锅;而在B句里,“它”指的则是日料。
还记得在本文Part 1里提到的那个“不要日本菜测试”么?我真的不是在强调“回字有四种写法”——这个测试的本质,是测试对话系统能不能使用简单逻辑来做推理(指代的是什么)。
而在Winograd Schema Challenge中,则是用世界知识(包括常识)来做推理:
如果系统不知道相应的常识(四川火锅是辣的;日料是不辣的),就没有推理的基础。更不用说推理还需要被准确地执行。
有人说,我们可以通过上下文处理来解决这个问题。不好意思,上面这个常识根本就没有出现在整个对话当中。不在“上文”里面,又如何处理?
对于这个部分的详细解释,请看下一章 (Part 3 对话的本质)。
尽管指代问题和逻辑问题,看上去,在应用方面已经足够致命了;但这些也只是深度学习表现出来的诸多局限性中的一部分。
哪怕更进一步,再过一段时间,有一家AI在Winograd Schema Challenge拿了100%的正确率,我们也不能期望它在自然语言处理中的表现如同人一样,因为还有更严重和更本质的问题在后面等着。
“ 对话系统更大的挑战不是 NLU ”
我们来看问题表现在什么地方。
现在我们知道了,当人跟现在的AI对话的时候,AI能识别你说的话,是靠深度学习对你说出的自然语言进行分类,归于设定好的意图,并找出来文本中有哪些实体。
而AI什么时候回答你,什么时候反问你,基本都取决于背后的“对话管理”系统里面的各种表上还有啥必填项没有填完。而问你的话,则是由产品经理和代码小哥一起手动完成的。
那么,这张表是谁做的?
或者说,是谁决定,对于“订机票”这件事,要考虑哪些方面?要获得哪些信息?需要问哪些问题?机器又是怎么知道的?
是人。是产品经理,准确点说。
就像刚才的“订机票”的案例,当用户问到“航司”的时候,之前的表里并没有设计这个概念,AI就无法处理了。
要让AI能处理这样的新条件,得在“订机票”这张表上,新增加“航空公司”一栏(黄色部分)。而这个过程,都得人为手动完成:产品经理设计好后,工程师编程完成这张表的编程。
所以AI并不是真的,通过案例学习就自动理解了“订机票”这件事情,包含了哪些因素。只要这个表还是由人来设计和编程实现的,在产品层面,一旦用户稍微谈及到表以外的内容,智障的情况就自然出现了。
因此,当Google duplex出现的时候,我并不那么关心 Google duplex发音和停顿有多像一个人——实际上,当我观察任意一个对话系统的时候,我都只关心1个问题:
“是谁设计的那张表:人,还是AI?”
只是,深度学习在对话系统里面,能做的只是识别用户讲出的那句话那部分——严格依照被人为训练的那样(监督学习)。至于其他方面,比如该讲什么话?该在什么时候讲话?它都无能为力。
但是真正人们在对话时的过程,却不是上面提到的对话系统这么设计的,而且相差十万八千里。人的对话,又是怎么开展的?这个差异究竟在哪里?为什么差异那么大?所谓深度学习很难搞定的地方,是人怎么搞定的呢?毕竟在这个星球上,我们自身就是70亿个完美的自然语言处理系统呢。
我们需要了解要解决的问题,才可能开展解决问题的工作。在对话领域,我们需要知道人们对话的本质是什么。下一章比较烧脑,我们将讨论“思维”这件事情,是如何主导人们的对话的。
Part 3
人类对话的本质:思维
“ 对话的最终目的是为了同步思维 ” 你是一位30出头的职场人士,每天上午9点半,都要过办公楼的旋转门,进大堂的,然后刷工牌进电梯,去到28楼,你的办公室。今天是1月6日,平淡无奇的一天。你刚进电梯,电梯里只有你一个人,正要关门的时候,有一个人匆忙挤进来。
进来的快递小哥,他进电梯时看到只有你们两人,就说了一声“你好”,然后又低头找楼层按钮了。
你很自然的回复:“你好”,然后目光转向一边。
两边都没什么话好讲——实际上,是对话双方认为彼此没有什么情况需要同步的。
人们用语言来对话,其最终的目的是为了让双方对当前场景模型(Situation model)保持同步。(大家先了解到这个概念就够了。更感兴趣的,详情请见 Toward a neural basis of interactive alignment in conversation )。
The interactive-alignment model (based on Pickering and Garrod, 2004)
上图中,A和B两人之间发展出来所有对话,都是为了让红框中的两个“Situation model” 保持同步。Situation model 在这里可以简单理解为对事件的各方面的理解,包括Context。
不少做对话系统的朋友会认为Context是仅指“对话中的上下文”,我想要指出的是,除此以外,Context还应该包含了对话发生时人们所处的场景。这个场景模型涵盖了对话那一刻,除了明文以外的所有已被感知的信息。 比如对话发生时的天气情况,只要被人感知到了,也会被放入Context中,并影响对话内容的发展。 A: “你对这个事情怎么看?”
B: “这天看着要下雨了,咱们进去说吧”——尽管本来对话内容并没有涉及到天气。
对同一件事情,不同的人在脑海里构建的场景模型是不一样的。 (想要了解更多,可以看 Situation models in language comprehension and memory. Zwaan, R. A., & Radvansky, G. A. (1998). )
所以,如果匆忙进电梯来的是你的项目老板,而且假设他和你(多半都是他啦)都很关注最近的新项目进展,那么你们要开展的对话就很多了。
在电梯里,你跟他打招呼:“张总,早!”, 他会回你 “早啊,对了昨天那个…”
不待他问完,优秀如你就能猜到“张总” 大概后面要聊的内容是关于新项目的,这是因为你认为张总对这个“新项目”的理解和你不同,有同步的必要。甚至,你可以通过昨天他不在办公室,大概漏掉了这个项目的哪些部分,来推理你这个时候应该回复他关于这个项目的具体什么方面的问题。 “昨天你不在,别担心,客户那边都处理好了。打款的事情也沟通好了,30天之内搞定。” ——你看,不待张总问完,你都能很棒的回答上。这多亏了你对他的模型的判断是正确的。
一旦你对对方的情景模型判断失误,那么可能完全“没打中点上”。 “我知道,昨天晚上我回了趟公司,小李跟我说过了。我是要说昨天晚上我回来办公室的时候,你怎么没有在加班呀?小王,你这样下去可不行啊…”
所以,人们在进行对话的过程中,并不是仅靠对方上一句话说了什么(对话中明文所包含的信息)就来决定回复什么。而这和当前的对话系统的回复机制非常不同。
“ 对话是思想从高维度向低维的投影 ”
我们假设,在另一个平行宇宙里,还是你到了办公楼。 今天还是1月6日,但2年前的今天,你与交往了5年的女友分手了,之后一直对她念念不忘,也没有交往新人。
你和往日一样,进电梯的,刚要关门的时候,匆忙进来的一个人,要关的门又打开了。就是你2年前分手的那位前女友。她进门时看到只有你们两,她抬头看了一下你,然后又低头找楼层电梯了,这时她说:“你好”。
请问你这时脑袋里是不是有很多信息汹涌而过?这时该回答什么?是不是类似“一时不知道该如何开口”的感觉?
这个感觉来自(你认为)你和她之间的情景模型有太多的不同(分手2年了),甚至你都无法判断缺少哪些信息。有太多的信息想要同步了,却被贫瘠的语言困住了。
在信息丰富的程度上, 语言是贫瘠的,而思想则要丰富很多 “Language is sketchy, thought is rich” (New perspectives on language and thought,Lila Gleitman, The Oxford Handbook of Thinking and Reasoning ;更多相关讨论请看, Fisher & Gleitman, 2002; Papafragou, 2007)
有人做了一个比喻:语言和思维的丰富程度相比,是冰山的一角。我认为远远不止如此: 对话是思想在低维的投影 。
如果是冰山,你还可以从水面上露出来的部分反推水下大概还有多大。属于维度相同,但是量不同。但是语言的问题在,只用听到文字信息,来反推讲话的人的思想,失真的情况会非常严重。
为了方便理解这个维度差异,在这儿用3D和2D来举例:思维是高维度(立体3D的形状),对话是低维度(2D的平面上的阴影)。如果咱们要从平面上的阴影的形状,来反推,上面悬着的是什么物体,就很困难了。两个阴影的形状一模一样,但是上面的3D物体,可能完全不同。
对于语言而言,阴影就像是两个 “你好”在字面上是一模一样的,但是思想里的内容却完全不同。在见面的那一瞬间,这个差异是非常大的: 你在想(圆柱):一年多不见了,她还好么?
前女友在想(球):这个人好眼熟,好像认识…
“ 挑战:用低维表达高维 ”
要用语言来描述思维有多困难?这就好比,当你试图给另一位不在现场的朋友,解释一件刚刚发生过的事情的时候,你可以做到哪种程度的还原呢?
试试用语言来描述你今天的早晨是怎么过的。
当你用文字完整描述后,我一定能找到一个事物或者某个具体的细节,它在你文字描述以外,但是却确实存在在你今天早晨那个时空里。
Source:The Challenger
比如,你可能会跟朋友提到,早饭吃了一碗面;但你一定不会具体去描述面里一共有哪些调料。传递信息时,缺少了这些细节(信息),会让听众听到那碗面时,在脑海里呈现的一定不是你早上吃的“那碗面”的样子。 这就好比让你用平面上(2D)阴影的样子,来反推3D的形状。你能做的,只是尽可能的增加描述的视角,尽可能给听众提供不同的2D的素材,来尽量还原3D的效果。
为了解释脑中“语言”和“思想”之间的关系(与读者的情景模型进行同步),我画了上面那张对比图,来帮助传递信息。如果要直接用文字来精确描述,还要尽量保全信息不丢失,那么我不得不用多得多的文字来描述细节。(比如上面的描述中,尚未提及阴影的面积的具体大小、颜色等等细节)。
这还只是对客观事物的描述。当人在试图描述更情绪化的主观感受时,则更难用具体的文字来表达。
比如,当你看到 Angelina Jordan这样的小女生,却能唱出I put a spell on you这样的歌 的时候,请尝试用语言精确描述你的主观感受。是不是很难?能讲出来话,都是类似“鹅妹子嘤”这类的?这些文字能代表你脑中的感受的多少部分?1%?
希望此时,你能更理解所谓 “语言是贫瘠的,而思维则要丰富很多”。
那么,既然语言在传递信息时丢失了那么多信息,人们为什么理解起来,好像没有遇到太大的问题?
“ 为什么人们的对话是轻松的? ”
假设有一种方式,可以把此刻你脑中的感受,以完全不失真的效果传递给另一个人。这种信息的传递和上面用文字进行描述相比,丰富程度会有多大差异?
可惜,我们没有这种工具。我们最主要的交流工具,就是语言,靠着对话,来试图让对方了解自己的处境。
那么,既然语言这么不精准,又充满逻辑上的漏洞,信息量又不够,那么人怎么能理解,还以此为基础,建立起来了整个文明?
比如,在一个餐厅里,当服务员说 “火腿三明治要买单了”,我们都能知道这和“20号桌要买单了”指代的是同样的事情 ( Nuberg,1978 )。是什么让字面上那么大差异的表达,也能有效传递信息?
人能通过对话,有效理解语言,靠的是解读能力——更具体的点,靠的是对话双方的共识和基于共识的推理能力。
当人接收到低维的语言之后,会结合引用常识、自身的世界模型(后详),来重新构建一个思维中的模型,对应这个语言所代表的含义。这并不是什么新观点,大家熟悉的开复老师,在1991年在苹果搞语音识别的时候,就在采访里科普,“ 人类利用常识来帮助理解语音 ”。
当对话的双方认为对一件事情的理解是一样的,或者非常接近的时候,他们就不用再讲。需要沟通的,是那些(彼此认为)不一样的部分。 当你听到“苹果”两个字的时候,你过去建立过的苹果这个模型的各个维度,就被引用出来,包括可能是绿或红色的、味道的甜、大概拳头大小等等。如果你听到对方说“蓝色的苹果”时,这和你过去建立的关于苹果的模型不同(颜色)。思维就会产生一个提醒,促使你想要去同步或者更新这个模型,“苹果为什么是蓝色的?”
还记得,在Part 2 里我们提到的那个测试指代关系的Winograd Schema Challenge么?这个测试的名字是根据 Terry Winograd的一个例子 而来的。 “议员们拒绝给抗议者颁发许可证,因为他们 [ 害怕/提倡 ] 暴力。”
当 [ 害怕 ] 出现在句子当中的时候,“他们”指的应该是议员们;当[ 提倡 ]出现在句子当中的时候,“他们”则指的是“抗议者”。
1. 人们能够根据具体情况,作出判断,是因为根据常识做出了推理,“议员 害怕 暴力;抗议者 提倡 暴力。”
2. 说这句话的人,认为这个常识对于听众应该是共识,就直接把它省略掉了。
同理,之前(Part 2)我们举例时提到的那个常识 (“四川火锅是辣的;日料不是辣的”),也在表达中被省略掉了。常识(往往也是大多数人的共识)的总量是不计其数,而且总体上还会随着人类社会发展的演进而不断新增。
例子1,如果你的世界模型里已经包含了“华农兄弟” (你看过并了解他们的故事),你会发现我在Part 2最开始的例子,藏了一个梗(做成叫花鸡)。但因为“华农兄弟”并不是大多数人都知道的常识,而是我与特定人群的共识,所以你看到这句话时,获得的信息就比其人多。而不了解这个梗的人,看到那里时就不会接收到这个额外的信息,反而会觉得这个表达好像有点点奇怪。
例子2,创投圈的朋友应该都有听说过 Elevator pitch,就是30秒,把你要做什么事情讲清楚。通常的案例诸如:“我们是餐饮界的Uber”,或者说“我们是办公室版的Airbnb”。这个典型结构是“XX版的YY”,要让这句话起到效果,前提条件是XX和YY两个概念在发生对话之前,已经纳入到听众的模型里面去了。如果我给别人说,我是“对话智能行业的麦肯锡”,要能让对方理解,对方就得既了解对话智能是什么,又了解麦肯锡是什么。
“ 基于世界模型的推理 ”
场景模型是基于某一次对话的,对话不同,场景模型也不同;而世界模型则是基于一个人的,相对而言长期不变。
对世界的感知,包括声音、视觉、嗅觉、触觉等感官反馈,有助于人们对世界建立起一个物理上的认识。对常识的理解,包括各种现象和规律的感知,在帮助人们生成一个更完整的模型: 世界模型 。
无论精准、或者对错,每一个人的世界模型都不完全一样,有可能是观察到的信息不同,也有可能是推理能力不一样。世界模型影响的是人的思维本身,继而影响思维在低维的投影:对话。
让我们从一个例子开始:假设现在咱们一起来做一个不那么智障的助理。我们希望这个助理能够推荐餐厅酒吧什么的,来应付下面这样的需求:
当用户说:“我想喝点东西”的时候,系统该怎么回答这句话?经过Part 2,我相信大家都了解,我们可以把它训练成为一个意图“找喝东西的店”,然后把周围的店检索出来,然后回复这句话给他:“在你附近找到这些选择”。
恭喜,咱们已经达到Siri的水平啦!
但是,刚刚我们开头就说了,要做不那么智障的助理。这个“喝东西的店”是奶茶点还是咖啡店?还是全部都给他?
嗯,这就涉及到了推理。我们来手动模拟一个。假设我们有用户的Profile数据,把这个用上:如果他的偏好中最爱的饮品是咖啡,就给他推荐咖啡店。
这样一来,我们就可以更“个性化”的给他回复了:“在你附近找到这些咖啡店”。
这个时候,咱们的AI已经达到了不少“智能系统”最喜欢鼓吹的个性化概念——“千人千面”啦!
然后我们来看这个概念有多蠢。
一个人喜欢喝咖啡,那么他一辈子的任意时候就都要喝咖啡么?人是怎么处理这个问题的呢?如果用户是在下午1点这么问,这么回他还好;如果是在晚上11点呢?我们还要给他推荐咖啡店么?还是应该给他推荐一个酒吧?
或者,除此之外,如果今天是他的生日,那么我们是不是该给他点不同的东西?或者,今天是圣诞节,该不该给他推荐热巧克力?
你看,时间是一个维度,在这个维度上的不同值都在影响给用户回复什么不同的话。
时间和用户的Profile不同的是:
1. 时间这个维度上的值有无限多;
2. 每个刻度还都不一样。比如虽然生日是同一个日期,但是过生日的次数却不重复;
除了时间这个维度以外,还有空间。
于是我们把空间这个维度叠加(到时间)上去。你会发现,如果用户在周末的家里问这个问题(可能想叫奶茶外卖到家?),和他在上班时间的办公室里问这个问题(可能想出去走走换换思路),咱们给他的回复也应该不同。
光是时空这两个维度,就有无穷多的组合 ,用"if then"的逻辑也没法全部手动写完。我们造机器人的工具,到这个需求,就开始捉襟见肘了。
何况时间和空间,只是世界模型当中最显而易见的两个维度。还有更多的,更抽象的维度存在,并且直接影响与用户的对话。比如,人物之间的关系;人物的经历;天气的变化;人和地理位置的关系(是经常来出差、是当地土著、是第一次来旅游)等等等等。咱们聊到这里,感觉还在聊对话系统么? 是不是感觉有点像在聊推荐系统 ?
要想效果更好,这些维度的因素都要叠加在一起进行因果推理,然后把结果给用户。
至此,影响人们对话的,光是信息(还不含推理)至少就有这三部分:明文(含上下文)+ 场景模型(Context)+ 世界模型。
普通人都能毫不费力地完成这个工作。但是深度学习只能处理基于明文的信息。对于场景模型和世界模型的感知、生成、基于模型的推理, 深度学习统统无能为力 。
这就是为什么现在炙手可热的深度学习无法实现真正的智能(AGI)的本质原因:不能进行因果推理。
根据世界模型进行推理的效果,不仅仅体现上在对话上,还能应用在所有现在成为AI的项目上,比如自动驾驶。
经过大量训练的自动驾驶汽车,在遇到偶发状况时,就没有足够的训练素材了。比如,突然出现在路上的婴儿车和突然滚到路上的垃圾桶,都会被视为障碍物,但是刹不住车的情况下,一定要撞一个的时候,撞哪一个?
又比如,对侯世达(Douglas Hofstardler )而言,“驾驶”意味着当要赶着去一个地方的时候,要选择超速还是不超速;要从堵车的高速下来,还是在高速上慢慢跟着车流走...这些决策都是驾驶的一部分。他说:“ 世界上各方面的事情都在影响着“驾驶”这件事的本质 ”。
“ 人脑有两套系统:系统 1 和 系统 2 ”
关于 “系统1和系统2”的详情,请阅读 Thinking, Fast and Slow, by Daniel Kahneman,一本非常好的书,对人的认知工作是如何展开的进行了深入的分析。在这儿,我给还不了解的朋友介绍一下,以辅助本文前后的观点。
心理学家认为,人思考和认知工作分成了两个系统来处理: 系统1是快思考:无意识、快速、不怎么费脑力、无需推理 系统2是慢思考:需要调动注意力、过程更慢、费脑力、需要推理 系统1先上,遇到搞不定的事情,系统2会出面解决。
系统1做的事情包括: 判断两个物体的远近、追溯声音的来源、完形填空 ( "我爱北京天安 " )等等。
顺带一提,下象棋的时候,一眼看出这是一步好棋,这个行为也是系统1实现的——前提是你是一位优秀的玩家。
对于中国学生而言,你突然问他:“7乘以7”,他会不假思索的说:“49!”这是系统1在工作,因为我们在小学都会背99乘法表。这个49并非来自计算结果,而是背下来的(反复重复)。
相应的,如果你问:“3287 x 2234等于多少?”,这个时候人就需要调用世界模型中的乘法规则,加以应用(计算)。这就是系统2的工作。
另外,在系统1所设定的世界里,猫不会像狗一样汪汪叫。若事物违反了系统1所设定的世界模型,系统2也会被激活。
在语言方面, Yoshua Bengio 认为系统1不做与语言有关的工作;系统2才负责语言工作 。对于深度学习而言,它更适合去完成系统1的工作, 实际上它根本没有系统2的功能 。
关于这两个系统,值得一提的是,人是可以通过训练,把部分系统2才能做的事情,变成系统1来完成的。比如中国学生得经过“痛苦的记忆过程”才能熟练掌握99乘法表,而不是随着出生到长大的自然经验,慢慢学会的。
但是这里有2个有意思的特征: 1. 变成系统1来处理问题的时候,可以节约能量。人们偏向相信自己的经验,是因为脑力对能量的消耗很大,这是一个节能的做法。
2. 变成系统1的时候,会牺牲辩证能力,因为系统1对于逻辑相关的问题一无所知。“我做这个事情已经几十年了”这种经验主义思维就是典型案例。
想想自己长期积累的案例是如何在影响自己做判断的?
“ 单靠深度学习搞不定语言,现在不行,将来也不行 ”
在人工智能行业里,你经常会听到有人这么说 “尽管当前技术还实现不了理想中的人工智能,但是技术是会不断演进的,随着数据积累的越来越多,终将会实现让人满意的人工智能。”
如果这个说法,是指寄希望于仅靠深度学习,不断积累数据量,就能翻盘——那就大错特错了。
无论你怎么优化“马车”的核心技术(比如更壮、更多的马),都无法以此造出汽车 (下图右)。
对于大众而言,技术的可演进性,是以宏观的视角看人类和技术的关系。但是发动机的演化和马车的关键技术没有半点关系。
深度学习领域的3大牛,都认为单靠深度学习这条路(不能最终通向AGI)。感兴趣的朋友可以沿着这个方向去研究: Geoffrey Hinton的怀疑 :“我的观点是都扔掉重来吧”
Yoshua Bengio的观点 :“如果你对于这个每天都在接触的世界,有一个好的因果模型,你甚至可以对不熟悉的情况进行抽象。这很关键......机器不能,因为机器没有这些因果模型。我们可以手工制作这些模型,但是这远不足够。我们需要能发现因果模型的机器。”
Yann LeCun的观点 :“A learning predictive world model is what we’re missing today, and in my opinion is the biggest obstacle to significant progress in AI.”
至于深度学习在将来真正的智能上扮演的角色,在这儿我 引用Gary Marcus 的说法:“I don’t think that deep learning won’t play a role in natural understanding, only that deep learning can’t succeed on its own.”
“ 解释人工智障产品 ”
现在,我们了解了人们对话的本质是思维的交换,而远不只是明文上的识别和基于识别的回复。而当前的人工智能产品则完全无法实现这个效果。那么当用户带着人类的世界模型和推理能力来跟机器,用自然语言交互时,就很容易看到破绽。 Sophia是一个技术上的骗局(凡是鼓吹Sophia是真AI的,要么是不懂,要么是忽悠);
现在的AI,都不会有真正的智能(推理能力什么的不存在的,包括Alpha go在内);
只要是深度学习还是主流,就不用担心AI统治人类;
对话产品感觉用起来智障,都是因为想跳过思维,直接模拟对话(而现在也只能这样);
“用的越多,数据越多,智能会越强,产品就会越好,使用就会越多”——对于任务类对话产品,这是一个看上去很酷,实际上不靠谱的观点;
一个AI agent,能对话多少轮,毫无意义;
to C的助理产品做不好,是因为解决不了“如何获得用户的世界模型数据,并加以利用”这个问题;
to B的对话智能公司为何很难规模化?(因为场景模型是手动生成的)
先有智能,后有语言 :要做到真正意义上的自然语言对话,至少要实现基于常识和世界模型的推理能力。而这一点如果能实现,那么我们作为人类,就可能真的需要开始担心前文提到的智能了。
不要用 NLP 评价一个对话智能产品 :年底了,有些媒体开始出各种AI公司榜单,其中有不少把做对话的公司分在NLP下面。这就好比,不要用触摸屏来衡量一款智能手机。在这儿我不是说触摸屏或者NLP不重要(Essential),反而因为太重要了,这个环节成为了每一家的标配,以至于在这方面基本已经做到头了,差异不过1%。
对于一个对话类产品而言,NLU尽管重要,但只应占个整体配件的5-10%左右。更进一步来说,甚至意图识别和实体提取的部分用大厂的,产品间差异也远小于对话管理部分的差距。 真正决定产品的是剩下的 90% 的系统。
到此,是不是有一种绝望的感觉?这些学界和行业的大牛都没有解决方案,或者说连有把握的思路都没有。是不是做对话智能这类的产品就没戏了?上限就是这样了么?
不是。对于一项技术而言,可能确实触底了;但是对于应用和产品设计而言,并不是由一个技术决定的,而是很多技术的结合,这里还有很大的空间。
作为产品经理,让我来换一个角度。我们来研究一下,既然手中的工具是这些,我们能用他们来做点什么?
Part 4
AI 产品的潜力在于设计
“ AI 的归 AI ,产品的归产品 ”
《The Prestige》2006,剧照 有一部我很喜欢的电影,The Prestige,里面讲了一个关于“瞬间移动”的魔术。对于观众而言,就是从一个地方消失,然后瞬间又从另一个地方出现。
第一个魔术师,成功的在舞台上实现了这个效果。他打开舞台上的右边的门,刚一进去的一瞬间,就从舞台左边的门出来了。对观众而言,这完全符合他们的期望。
第二个魔术师在观众席里,看到效果后惊呆了,他感觉这根本毫无破绽。但是他是魔术师——作为一个产品经理——他就想研究这个产品是怎么实现的。但是魔术行业里,最不受人待见的,就是魔术揭秘。
影片最后,他得到了答案(剧透预警):所有的工程机关、升降机、等等,都如他所料的藏在了舞台下面。但真正的核心是,第一个魔术师一直隐藏着自己的另一个双胞胎兄弟。当他打开一个门,从洞口跳下舞台的那一刻,双胞胎的另一位就马上从另一边升上舞台。
看到这里,大家可能就恍然大悟:“ 原来是这样,双胞胎啊!”
这感觉是不是有点似曾相识?在本文Part 2,我们聊到把对话系统的黑箱打开,里面就是填一张表的时候,是不是有类似的感觉?对话式人工智能的产品(对话系统)就像魔术,是一个黑箱,用户是以感知来判断价值的。
“ 我还以为有什么黑科技呢,我是双胞胎我也可以啊。”
其实这并不容易。我们先不说魔术的舞台里面的工程设计,这个魔术最难的地方是如何能在魔术师的生活中,让另一个双胞胎在大众视野里完全消失掉。如果观众们都知道魔术师是双胞胎,就很可能猜到舞台上的魔术是两个人一起表演的。所以这个双胞胎,一定不能出现在大众的“世界模型”里。
为了让双胞胎的另一个消失在大众视野里,这两兄弟付出了很多代价,身心磨,绝非一般人能接受的,比如共享同一个老婆。
这也是我的建议: 技术不够的时候,设计来补 。做AI产品的同学,不要期待给你智能。要是真的有智能了,还需要你干什么?人工智能产品经理需要设计一套庞大的系统,其中包括了填表、也当然包括深度学习带来的意图识别和实体提取等等标准做法、也包括了各种可能的对话管理、上下文的处理、逻辑指代等等。
这些部分,都是产品设计和工程力量发挥的空间。
“ 设计思路的基础 ”
我需要强调一下,在这里,咱们讲的是AI产品思路,不是AI的实现思路。
对于对话类产品的设计,以现在深度学习的基础, 语义理解应该只占整个产品的5%-10% ;而其他的,都是想尽一切办法来模拟“传送”这个效果——毕竟我们都知道,这是个魔术。如果只是识别就占了你家产品的大量心血,其他的不去拉开差异,基本出来就是智障无疑。
在产品研发方面上,如果研发团队能提供多种技术混用的工具,肯定会增加开发团队和设计的发挥空间。这个做法也就是 DL(Deep Learning) + GOFAI (Good Old Fashioned AI) 的结合 。GOFAI是 John Haugeland首先提出的 ,也就是深度学习火起来之前的symbolic AI,也就是专家系统,也就是大多数在AI领域的人都看不起的 “if then…”
DL+GOFAI 这个前提,是当前一切后续产品设计思路的基础 。
“ Design Principle :存在即为被感知 ”
“ 存在即为被感知 ” 是18世纪的哲学家George Berkeley的名言。加州大学伯克利分校的命名来源也是为了纪念这位唯心主义大师。这个意思呢,就是如果你不能被感知到,你就是不存在的!
我认为“存在即为被感知” 是对话类AI产品的Design principle。 对话产品背后的智能,是被用户感知到而存在的 。直到有一天AI可以代替产品经理,在那之前,所有的设计都应该围绕着,如何可以让用户感觉和自己对话的AI是有价值的,然后才是聪明的。
要非常明确自己的目的, 设计的是AI的产品,而不是AGI本身 。就像魔术的设计者,给你有限的基础技术条件,你能组装出一个产品,体验是人们难以想到。
同时,也要深刻的认识到产品的局限性。魔术就是魔术,并不是现实。
这意味着,在舞台上的魔术,如果改变一些重要的条件,它就不成立了。比如,如果让观众跑到舞台的顶上,从上往下看这个魔术,就会发现舞台上有洞。或者“瞬间移动”的不是这对双胞胎中的一个,而是一个观众跑上去说,“让我来瞬间移动试试”,就穿帮了。
Narrow AI的产品,也是一样的。如果你设计好了一个Domain,无论其中体验如何,只要用户跑到Domain的边界以外了,就崩溃了。先设定好产品边界,设计好“越界时给用户的反馈”,然后在领域里面,尽可能的模拟这个魔术的效果。
假设Domain的边界已经设定清晰了,哪些方面可以通过设计和工程的力量,来大幅增加效果呢?
其实,在“Part 3 对话的本质” 里谈到的与思维相关的部分,在限定Domain的前提下,都可以作为设计的出发点:你可以用GOFAI来模拟世界模型、也可以模拟场景模型、你可以Fake逻辑推理、可以Fake上下文指代——只要他们都限定在Domain里。
“ 选择合适的 Domain ”
成本(工程和设计的量)和给用户的价值并不是永远成正比,也根据不同的Domain的不同。
比如,我认为现在所有的闲聊机器人都没有什么价值。开放Domain,没有目标、没有限定和边界,对用户而言,会认为什么都可以聊。但是其自身“场景模型”一片空白,对用户所知的常识也一无所知。导致用户稍微试一下,就碰壁了。我把这种用户体验称为 “每次尝试都容易遇到挫折”。
可能,有些Domain对回复的内容并不那么看重。也就并不需要那么强壮的场景模型和推理机制来生成回复内容。
我们假设做一个“树洞机器人”,可以把产品定义是为,扮演一个好的听众,让用户把心中的压力烦恼倾诉出来。
Human Counseling. Source: Bradley University Online
这个产品的边界,需要非常明确的,在用户刚刚接触到的时候,强化到用户的场景模型中。主要是系统通过一些语言的反馈,鼓励用户继续说。而不要鼓励用户来期望对话系统能输出很多正确且有价值的话。当用户做出一些陈述之后,可以跟上一些对“场景模型”依赖较小,泛泛的话。 “我从来没有这么考虑过这个问题,你为什么会这么想呢?”
“关于这个人,你还有哪些了解?”
“你觉得他为什么会这样?”
……
这样一来,产品在需求上,就大幅减轻了对“自然语言生成”的依赖。因为这个产品的价值,不在回复的具体内容是否精准,是否有价值上。这就同时降低了对话背后的“场景模型”、“世界模型”、以及“常识推理”这些高维度模块的需求。训练的素材嘛,也就是某个特定分支领域(比如职场、家庭等)的心理咨询师的对话案例。产品定义上,这得是一个Companion型的产品,不能真正起到理疗的作用。
当然,以上并不是真正的产品设计,仅仅是用一个例子来说明,不同的Domain对背后的语言交互的能力要求不同,进而对更后面的“思维能力”要求不同。选择产品的Domain时,尽量远离那些严重依赖世界模型和常识推理,才能进行对话的场景。
有人可能说,你这不就是Sophia的做法么?不是。这里需要强调的是Sophia的核心问题是欺骗。产品开发者是想忽悠大众,他们真的做出了智能。
在这里,我提倡的是明确告诉用户,这就是对话系统,而不是真的造出了智能。这也是为什么,在我自己的产品设计中,如果遇到真人和AI同时为用户服务的时候(产品上称为Hybrid Model),我们总是会偏向明确让用户知道,什么时候是真人在服务,什么时候是机器人在服务。这么做的好处是,控制用户的预期,以避免用户跑到设计的Domain以外去了;不好的地方是,你可能“听上去”没有那么酷。
所以,当我说“存在即为被感知”的时候,强调的是对价值的感知;而不是对“像人一样”的感知。
“ 对话智能的核心价值:在内容,不在交互 ”
多年前,还在英国读书的时候,我曾经在一个非常有名历史悠久的秘密结社里工作。我对当时的那位照顾会员需求的大管家印象深刻。你可以想象她好像是“美国运通黑卡服务”的超级礼宾,她有两个超能力:
1. Resourceful,会员的奇葩需求都能想尽办法的实现:一个身在法兰克福的会员半夜里遇到急事,临时想尽快回伦敦,半夜没有航班了,打电话找到大管家求助。最后大管家找到另一个会员的朋友借了私人飞机,送他一程,凌晨回到了伦敦。
2. Mind-reading,会员想要什么,无需多言:
“Oliver,我想喝点东西…”
“当然没问题,我待会给你送过来。” 她也不需要问喝什么,或者送到哪里。
人人都想要一个这样的管家。蝙蝠侠需要Alfred;钢铁侠需要Javis;西奥多需要Her(尽管这哥们后来走偏了);iPhone 需要Siri;这又回到了我们在Part1里提到的,AI的to C 终极产品是智能助理。
但是,人们需要这个助理的根本原因,是因为人们需要它的对话能力么?这个世界上已经有70亿个自然语言对话系统了(就是人),为什么我们还需要制造更多的对话系统?
我们需要的是对话系统后面的思考能力,解决问题的能力 。而对话,只是这个思考能力的交互方式(Conversational User Interface)。如果真能足够聪明的把问题提前解决了,用户甚至连话都不想说。
我们来看个例子。
我知道很多产品经理已经把这个iPhone初代发布的东西讲烂了。但是,在这儿确实是一个非常好的例子:我们来探讨一下iPhone用虚拟键盘代替实体键盘的原因。
普通用户,从最直观的视角,能得出结论:这样屏幕更大!需要键盘的时候就出现,不需要的时候就消失。而且还把看上去挺复杂的产品设计给简化了,更好看了。甚至很多产品经理也是这么想的。实际上,这根本不是硬件设计的问题。原因见下图。
其实乔布斯在当时也讲的很清楚:物理键盘的核心问题是,(作为交互UI)你不能改变它。物理交互方式(键盘)不会根据不同的软件发生改变。
如果要在手机上加载各种各样的内容,如果要创造各种各样的软件生态,这些不同的软件都会有自己不同的UI,但是交互方式都得依赖同一种(物理键盘无法改变),这就行不通了。
所以,实际代替这些物理键盘的,不是虚拟键盘,而是整个触摸屏。因为iPhone(当时的)将来会搭载丰富的生态软件内容,就必须要有能与这些还没出现的想法兼容的交互方式。
在我看来,上述一切都是为了丰富的内容服务。再一次的,交互本身不是核心,它背后搭载的内容才是。
但是在当初看这个发布会的时候,我是真的没有get到这个点。那个时候真的难以想象,整个移动互联时代会诞生的那么多APP,都有各自不同的UI,来搭载各式各样的服务。
你想想,如果以上面这些实体键盘,让你来操作大众点评、打开地图、Instagram或者其他你熟悉的APP,是一种怎样的体验?更有可能的是,只要是这样的交互方式,根本设计不出刚才提到的那些APP。
与之同时,这也引申出一个问题:如果设备上,并没有多样的软件和内容生态,那还应该把实体键设计成触摸和虚拟的方式么?比如,一个挖掘机的交互方式,应该使用触屏么?甚至对话界面?
“ 对话智能解决重复思考 ”
同样的,对话智能的产品的核心价值,应该在解决问题的能力上,而不是停留在交互这个表面。这个“内容” 或者 “解决问题的能力” 是怎么体现的呢?
工业革命给人类带来的巨大价值在于解决“重复体力劳动”这件事。
经济学家Tyler Cowen 认为,“ 什么行业的就业人越多,颠覆这个工种就会创造更大的商业价值。” 他在Average Is Over这本书里描述到: “ 20世纪初,美国就业人口最多的是农民;二战后的工业化、第三产业的发展,再加上妇女解放运动,就业人工最多的工种变成辅助商业的文字工作者比如秘书助理呼叫中心(文员,信息输入)。1980/90年代的个人计算机,以及Office 的普及,大量秘书,助理类工作消失。”
这里提及的工作,都是需要大量重复的工作。而且不停的演变, 从重复的体力,逐步到重复的脑力。
从这个角度出发,对一个场景背后的“思考能力”没有把控的AI产品,会很快被代替掉。首当其冲的,就是典型意义上的智能客服。
在市场上,有很多这样的智能客服的团队,他们能够做对话系统(详见Part 2),但是对这各领域的专业思考,却不甚了解。
我把“智能客服” 称为“前台小姐姐”——无意冒犯,但是前台小姐姐的主要工作和专业技能并没有关系。他们最重要的技能就是对话,准确点说是用对话来“路由”——了解用户什么需求,把不合适的需求过滤掉,再把需求转给专家去解决。
但是对于一个企业而言,客服是只嘴和耳,而专家才是脑,才是内容,才是价值。客服有多不核心?想想大量被外包出去的呼叫中心,就知道了。
与这类客服机器人产品对应的,就是专家机器人。一个专家,必定有识别用户需求的能力,反之不亦然。你可以想象一个企业支付给一个客服多少薪资,又支付给一个专家多少薪资?一个专家需要多少时间培训和准备才能上岗,客服小姐姐呢?于此同时, 专业能力是这个机构的核心,而客服不是 。
正因为如此,很多人认为,人工的呼叫中心,以后会被AI呼叫中心代替掉;而我认为,用AI做呼叫中心的工作,是一个非常短暂的过渡型方案。很快代替人工呼叫中心的,甚至代替AI呼叫中心,是具备交互能力的专家AI中心。在这儿,“专家”的意义大于“呼叫”。
在经历过工具化带来的产能爬坡和规模效应之后,他们成本差不多,但是却专业很多。比如他直接链接后端的供给系统的同时,还具备专业领域的推理能力,也能与用户直接交互。
NLP在对话系统里解决的是交互的问题。
在人工智能产品领域里,给与一定时间,掌握专业技能的团队一定能对话系统; 而掌握对话系统的团队则很难掌握专业技能 。试想一下在几年前,移动互联刚刚出现的时候,会做app的开发者,去帮银行做app;而几年之后银行都会自己开发app,而开发者干不了银行的事。
在这个例子里,做AI产品定义的朋友,你的产品最好是要代替(或者辅助)某个领域专家;而不要瞄准那些过渡性岗位,比如客服。
从这个角度出发, 对话智能类的产品最核心的价值,是进一步的代替用户的重复思考。 Work on the mind not the mouth. 哪怕已经是在解决脑袋的问题, 也尽量去代替用户系统 2 的工作,而不只是系统 1 的工作。
在你的产品中,加入专业级的推理;帮助用户进行抽象概念与具象细节之间的转化;帮助用户去判断那些出现在他的模型中,但是他口头还没有提及的问题;考虑他当前的环境模型、发起对话时所处的物理时空、过去的经历;推测他的心态,他的世界模型。
先解决思考的问题,再尽可能的转化成语言。
Part 5
AIPM
“ 缺了什么? ”
2018年10月底,我在慕尼黑为企业客户做on site support。期间与客户的各个BU、市场老板们以及自身的研发团队交流对话AI的应用。作为全球最顶尖的汽车品牌之一,他们也在积极寻求AI在自身产品和服务上的应用。 不缺技术人才。 尽管作为传统行业的大象,可能会被外界视为不擅长AI,其实他们自身并不缺少NLP的研发。当我跟他们的NLP团队交流时,发现基本都有世界名校的PHD。而且,在闭门的供应商大会上,基本全球所有的科技大厂和咨询公司都在场了。就算实在搞不了,也大有人排着队的想帮他们搞。
创新的意愿强烈 。在我接触过的大企业当中,特别是传统世界100强当中中,这个巨头企业是非常重视创新的。经过移动互联时代,丢掉的阵地,他们是真心想一点点抢回来,并试图领导所在的行业,而不是follow别人的做法。不仅仅是像“传统的大企业创新”那样做一些不痛不痒的POC,来完成创新部门的KPI。他们则真的很积极地推进AI的商业化,而且勇于尝试改变过去和Tech provider之间的关系。这点让我印象深刻,限于保密条款,在此略过细节。(关于国际巨型企业借新技术的初创团队之手来做颠覆式创新,也是一个很有意思的话题,以后新开一个Topic。)
数据更多。 那么传统巨头的优势就在于,真正拥有业务场景和实际的数据。卖出去的每一台产品都是他们的终端,而且开始全面联网和智能化。再加上,各种线下的渠道、海量的客服,其实他们有能力和空间来搜集更完整的用户生命周期数据。
当然,作为硬币的另一面,百年品牌也自然会有严重的历史牵绊。机构内部的合规、采购流程、数据的管控、BU之间的数据和行政壁垒也是跑不掉的。这些环节的Trade off确实大大的影响了对上述优势的利用。
但是最缺少的还是产品定义能力。
如果对话智能的产品定义失败,后面的执行就算是完美的,出来的效果也是智障。有些银行的AI机器人就是例子:立项用半年,竞标用半年,开发用一年,然后上线跑一个月就因为太蠢下线了。
但这其实并 不是传统行业的特点,而是目前所有玩家的问题 ——互联网或科技公司的对话AI产品也逃不掉。可能互联网企业还自我感觉良好,在这产品设计部分,人才最不缺了——毕竟“人人都是产品经理” 嘛。但在目前,咱们看到的互联网公司出来的产品也都是差不多的效果,具体情况咱们在Part 2里已经介绍足够多了。
我们来看看难点在哪里。
AI产品该怎么做定义呢?也就是,需要怎样的产品才能实现商业需求。技术部门往往主要关注技术实现,而不背商业结果KPI;而业务部门的同事对AI的理解又很有限,也就容易提出不合适的需求。
关键是,在做产品定义时,你想要描述 “我想要一个这样的AI,它可以说…” 的时候你会发现,因为是对话界面,你根本无法穷尽这个产品的可能性。其中一个具体细节就是,产品文档该怎么写,这就足够挑战了。
“ 对话 AI 产品的管理方法 ”
先给结论:如果还想沿用管理GUI产品的方法论来管理对话智能产品,这是不可能的。
从行业角度来看,没有大量成功案例,就不会有流水线;没有流水线,就没有基于流水线的项目管理。
也就是说,从1886年开始第一辆现代汽车出现,到1913年才出现第一条流水线——中间有27年的跨度。再到后来丰田提出The Toyota Way,以精益管理(Lean Management)来快速迭代(类似敏捷开发)以尽量避免浪费,即 Kaizen(改善) ,这已经是2001年的事情了。
这两天和其他也在给大企业做对话的同行交流的时候,听到很多不太成功的产品案例,归结起来几乎都是因为 “产品Scope定义不明”,导致项目开展到后面根本收不了尾。而且因为功能之间的耦合紧密,连线都上不了(遇到上下文对话依赖的任务时,中间环节一但有缺失,根本走不通流程)。这些都是行业早期不成熟的标志。
“ 对话 AI 产品的 Design Principle 尚未出现 ”
对话智能领域相对视觉类的产品,有几个特性上的差异:
1)是产品化远不如视觉类AI成熟;
2)深度学习在整个系统里扮演的角色虽然重要,但是还是很少,远不够撑起来有价值的对话系统;
3)产品都是黑箱,目前在行业中尚无比较共同认可的设计标准。
APP发展到后面,随着用户的使用习惯的形成,和业界内成功案例的“互相交流”,逐步形成了一些设计上的共识,比如下面这一排,最右边红圈里的 “我”:
但是,从2007年iPhone发布,到这些移动产品的设计规范逐步形成, 也花了近6、7年时间,且不提这是图形化界面。
到如今,这类移动设备上的产品设计标准已经成熟到,如果在设计师不遵循一些设计思路,反而会引起用户的不习惯。只是对话系统的设计规范,现在谈还为时尚早。
到这里,结合上述两个点(对话AI产品的管理方法、设计规范都不成熟),也就可以解释 为什么智能音箱都不智能 。因为智能音箱的背后都是一套“技能打造框架”,给开发者,希望开发者能用这套框架来制作各种“技能”。
而 “ 对话技能类平台 ” 在目前根本走不通 。任何场景一旦涉及到明文识别以外的,需要对特定的任务和功能进行建模,然后再融合进多轮对话管理里的场景,以现在的产品成熟程度,都无法抽象成有效的设计规范。现在能抽象出来的,都是非常简单的上下文管理(还记得Part 2里的“填表”么?)。
我就举一个例子,绝大部分的技能平台,根本就没有“用户生命周期管理”的概念。这和服务流程是两码事,也是很多机器人智障的诸多原因之一。因为涉及到太细节和专业的部分,咱们暂且不展开。
也有例外的情况:技能全部是语音控制型,比如“关灯开灯” “开空调25度”。这类主要依赖明文识别的技能,也确实能用框架实现比较好的效果。但这样的问题在于,开放给开发者没有意义:这类技能既不需要多样的产品化;开发者从这类开发中也根本赚不到钱——几乎没有商业价值。
另一个例外是大厂做MLaaS类平台,这还是很有价值的。能解决开发者对深度学习的需求,比如意图识别、分词、实体提取等最底层的需求。但整个识别部分,就如我在Part 3&4里提到的,只应占到任务对话系统的10%,也仅此而已。剩下的90%的工作,也是真正决定产品价值的工作,都得开发者自己搞。
他们会经历些什么?我随便举几个最简单的例子(行业外的朋友可以忽略): 如果你需要训练一个意图,要生成1000句话来做素材,那么“找100个人,每人写10句” 的训练效果要远好于 “找10个人,每人写100句”;
是用场景来分意图、用语义来分意图和用谓语来拆分意图,怎么选?这不仅影响机器人是否能高效支持“任务”之间的跳转,还影响训练效率、开发成本;
有时候意图的训练出错,是训练者把自己脑补的内容放进去了;
话术的重要性,不仅影响用户看着舒不舒服,更决定了他的回复的可能性——以及回复的回复的可能性——毕竟他说的每一句后面的话,都需要被识别后,再回复;
如果你要给一个电影院做产品,最好用图形化界面,而不要用语言来选座位:“现在空着的座位有,第一排的1,2,3,4….”
这些方面的经验和技巧数都数不完,而且还是最浅显、最皮毛的部分。你可以想象,对话智能的设计规范还有多少路要走——记得,每个产品还是黑箱,就算出了好效果,也看不到里面是怎么设计的。
“ 一个合适的 AIPM ”
当真正的人工智能实现之后,所有产品经理所需要做的思考,都会被AI代替。所以,真正的人工智能也许是人类最后的一个发明。在那一天之前,对话智能产品经理的工作,是使用各种力量来创造智能给人的感觉。
AIPM一定要在心中非常明确 “AI的归AI,产品的归产品”。做工具的和用工具的,出发点是完全不同。应该是带着做产品的目的,来使用AI;千万不要出现“AIPM是来实现AI的”这样的幻觉。
我们都熟悉,PM需要站在“人文和技术的十字路口”来设计产品。那么对话智能的AIPM可能在这方面可能人格分裂的情况更极端,以至于甚至需要2个人来做配合成紧密的产品小组——我认为一个优秀的对话智能产品经理,需要在这三个表现优秀:
1. 懂商业:就是理解价值。
对话产品的价值一定不在对话上,而是通过对话这种交互方式(CUI)来完成背后的任务或者解决具体问题。一个本来就很强的APP,就不要想着去用对话重新做一遍。反而是一些APP/WEB还没有能很好解决的问题,可以多花点时间研究看看。
这方面在Part 4 里的 对话智能的核心价值 部分,当中有详细阐述,在这里就不重复了。
2. 懂技术:理解手中的工具(深度学习 + GOFAI )
一个大厨,应该熟悉食材的特性;一个音乐家,应该熟悉乐器的特征;一个雕塑家,应该熟悉手中的凿子。大家工具都差不多,成果如何,完全取决于艺术家。
现在,AIPM手中有深度学习,那么就应该了解它擅长什么和不擅长什么。以避免提出太过于荒谬的需求,导致开发的同学向你发起攻击。了解深度学习的特性,会直接帮助我们判断哪些产品方向更容易出效果。比如,做一个推荐餐厅的AI,就比做一个下围棋的AI难太多了。
下围棋的产品成功,并不需要人类理解这个过程,接受这个结果就行。而推荐一个餐厅给用户,则必须要去模拟人的思维后,再投其所好。
人们在想要推荐餐厅的时候,通过对话,了解他的需求(绝对不能问太多,特别是显而易见的问题,比如他在5点的时候,你问他要定几点的餐厅)
对于围棋而言,每次(单次)输入的可能性只有不超过棋盘上19x19=361种可能性;一局棋的过程尽管千变万化,我们可以交给深度学习的黑箱;最后决定输赢所需要的信息,全部呈现在棋盘上的落子上,尽管量大,但与落子以外的信息毫无关系,全在黑箱里,只是这个黑箱很大。最后,输出的结果的可能性只有两种:输或者赢。
对于推荐餐厅。每次输入的信息,实际并不包含决策所需要的全部信息(无法用语言表达所有相关的影响因素,参考Part 3 里世界模型部分);而且输出的结果是开放的,因为推荐的餐厅,既不可被量化,更不存在绝对的对错。
了解CUI的特性后,不该用对话的就不要强上对话交互;有些使用对话成本非常高,又很不Robust的环节,同时用户价值和使用频次又很低的,就要考虑规避——咱们是做产品的,不是实现真正的AI的,要分清楚。
3. 懂人:心理和语言
这可能是当前对话类产品最重要的地方,也是拉开和其他产品设计的核心部分。也可能是中年人做产品的第二春。
对心理的理解 ,指的是当用户在说话的时候,对他脑中的模型的理解。英文中“Read the room”就是指讲话之前,先观察一下了解周围听众的情况,揣摩一下他们的心理,再恰当的说话。
比如,讲话的时候,是否听众开始反复的看表?这会让直接影响对话的进程。你有遇到过和某人对话起来感觉很舒服的么?这个人,不仅仅是语言组织能力强,更重要的则是他对你脑中的对话进程的把握,以及场景模型,甚至对你的世界模型有把握。他还知道怎么措辞,会更容易让你接受,甚至引导(Manipulate)你对一些话题的放弃,或者是加强。
对话系统的设计也是一样的。哪些要点在上文中说过?哪些类型的指代可以去模拟?如果是文字界面,用户会不会拉回去看之前的内容?如果是语音界面,用户脑中还记不记得住?如果记得住,还强调,会感觉重复;如果记不住,又不重复,会感觉困惑。
对语言的理解 ,则是指对口语特性的理解。我知道Frederick Jelinek说的“每当我开除一个语言学家,Speech识别的准确率就会增高”。只是,现在根本没有真正意义上的自然语言生成(NLG), 因为没有真正的思维生成 。
所以,任务类的对话的内容,系统不会自然产生,也无法用深度学习生成。对于AIPM而言,要考虑的还是有很多语言上的具体问题。一个回复里,内容会不会太长?要点该有几个?谓语是否明确,用户是否清晰被告知要做什么?条件又是什么?这样的回复,能引发多少种可能的问询?内容措辞是否容易引起误解(比如因为听众的背景不同,可能会有不同的解读)?
从这个角度而言,一个好的对话系统,必定出自一个很能沟通的人或者团队之手。能为他人考虑,心思细腻,使用语言的能力高效,深谙人们的心理变化。对业务熟悉,能洞察到用户的Context的变化,而其格调又帮助用户控制对话的节奏,以最终解决具体问题。
Part 6
可见的未来是现状的延续
“ 过渡技术 ”
在几周前,我与行业里另一家做对话的CEO讨论行业的将来。当我聊到“深度学习做对话还远达不到效果”的态度时,他问我:“如果是悲观的,那么怎么给团队希望继续往前进呢?”
其实 我并不是悲观的,可能只是更客观一点 。
既然深度学习在本质上搞不定对话,那么现在做对话AI的实现方式,是不是个过渡技术?这是一个好问题。
我认为, 用现在的技术用来制作AI的产品,还会持续很长时间,直到真正智能的到来 。
如果是个即将被替代或者颠覆的技术,那就不应该加码投入。如果可以预见未来,没人想在数码相机崛起的前期,加入柯达;或者在LED电视普及之前,重金投入在背投电视的研发上。而且难以预测的不仅仅是技术,还有市场的发展趋势。比如在中国,作为无现金支付方式,信用卡还没来得及覆盖足够多的支付场景,就被移动支付断了后路。
而现在的对话智能所使用的技术,还远没到这个阶段。
Clayton M. Christensen在《创新者的窘境》里描述了每个技术的三个阶段: 第一个阶段,缓步爬坡;
第二个阶段开始迅猛发展,但是到接近发展的高地(进步减速)的时候,另一个颠覆式技术可能已经悄悄萌芽,并重复着第一个技术的发展历程;
第三个阶段,则进入发展瓶颈,并最终被新技术颠覆
下图黑色部分,为书中原图:
而当前对话AI的技术,还在第一阶段(蓝色旗帜位置)称不上是高速发展,还处于探索的早期。黑箱的情况,会使得这个周期(第一阶段)可能比移动时代更长。
以当前的技术发展方向,结合学术界与工业界的进展来看,第二个技术还没有出现的影子。
但是同样因为深度学习在对话系统中,只扮演的一小部分角色,所以大部分的空间,也是留给大家探索和成长的空间。换句话来讲,还有很多发展的潜力。
前提是,我们在讨论对话类的产品,而不是实现AI本身。只是,这个阶段的对话AI,还不会达到人们在电影里看到的那样,能自如的用人类语言沟通。
2) 服务提供者崛起的机会
因为上述的技术发展特点,在短期的将来, 数据和设计是对话智能类产品的壁垒,技术不是 。
只是这里说的数据,不是指的用来训练的数据。而是供给端能完成服务的数据;能够照顾用户整个生命周期的数据;是当对话发生的时候,用户的明文以外的数据这些数据;影响用户脑中的环境模型、影响对任务执行相关的常识推理数据,等等。
而随着IOT的发展,服务提供者,作为与用户在线下直接打交道的一方,是最有可能掌握这些数据。他们能在各个Touch point去部署这些IOT设备,来搜集环境数据。并且,由他们决定要不要提供这些数据给平台方。
但是,往往这些行业里的玩家都是历史悠久、行动缓慢。其组织机构庞大,而且是组织结构并不是为了创新而设计,而是围绕着如何能让庞大的躯干不用思考,高速执行。而这也正是互联网企业和创业企业的机会。
3)超级终端与入口之争
对话智能类的产品必须搭载在硬件终端上。很多相关的硬件尝试,都是在赌哪个设备能够成为继手机之后的下一个超级终端。就好像智能手机作为计算设备,代替了PC的地位一样。
毕竟,在移动时代,抢到了超级终端,就抢到了用户获取服务的入口。在入口的基础之上,才是各个应用。
如果对话智能发展到足够好的体验,并能覆盖更多的服务领域时,哪一个终端更有可能成为下一个超级终端呢?智能音箱、带屏幕的音箱、车载设备甚至车机、穿戴设备等等都可以搭载对话智能。在5G的时代,更多的计算交给云端,在本地设备上留下能耗较低的OS和基础设施,I/O交给麦克风和音频播放就完成了。
credit:Pixabay
因此 任意一个联网设备,都可能具备交互和传递服务的能力 ,进一步削弱超级终端的存在。也就是说,作为个人用户,在任意一个联网设备上,只要具备语音交互和联网能力,都可能获得服务。特别是一些场景依赖的商业服务,如酒店、医院、办公室等等。
随着这些入口的出现,在移动时代的以流量为中心的商业模式,可能将不再成立。而新的模式可能诞生,想象一下,每一个企业,每一个品牌都会有自己的AI。一个或是多个,根据不同的业务而产生;对内部员工服务或者协助其工作,同时也接待外部的客服,管理整个生命周期从注册成这家企业的用户开始,到最后(不幸地)中断服务为止。
只是这个发展顺序是,先有服务,再有对话系统——就好像人,是有脑袋里的想法,再用对话来表达。
结语
在本文中,所有与技术和产品相关的讨论,都是在强调一个观点:一个产品是由很多技术组合而成。我不希望传达错误的想法,类似“深度学习不重要”之类的;相反,我是希望每一类技术都得到正确的认识,毕竟我们离真正的人工智能还有距离,能用上的都有价值。
作为AI从业者,心中也会留有非理性的希望,能早日见证到人造的智能的到来。毕竟,如果真正的智能出现了,可能产品经理(以及其他很多岗位)就彻底解放了(或者被摧毁了)。
这或许就是人类的最后一个发明。
本文开始于慕尼黑,最终成稿于北京,断断续续耗时接近3个月。期间与很多大企业,行业内的创业者,还有一些资本的同学沟通交流。在此表示感谢,就不一一点名啦。
关于作者
作者Mingke,正在从事对话智能方面的创业,为世界一百强企业提供对话智能应用的咨询和解决方案。上次《为什么现在的人工智能助理都像人工智障》一文发出来之后,认识结交了不少行业内的朋友。希望这次,把过去一段时间的思考与大家分享,能给行业内的新老朋友们一些启发,有兴趣沟通和碰撞的也欢迎与我联系。
欢迎行业里的新老朋友勾搭吐槽,微信:mingke27 (请注明称呼+所在机构)
产业 深度学习图灵测试语音合成对话系统
人工智能
2019-02-21 17:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
更多接单,发包信息请关注公众号:开源众包
和众包的江湖缘份
我们是盛茂网络科技有限公司。2018年10月份开始接触开源众包这个平台,此平台相较于其他平台的优势就是良好的平台维护,因此我们对于这个平台十分信赖。11月14日,我们迎来了平台的第一单。此后一个月,陆陆续续签了四五单。从此踏上了接单的漫漫不归路。一开始的优质服务商,到如今的服务之星,我们和开源众包一起成长。
我们的江湖经验
其实所谓经验,都是在错过一个个的单之后一步步总结出来的。在不断的反思中获得经验,明确前行的方向。
接单前:
总结为九个字:厚脸皮,常联系,快梳理。
所谓厚脸皮,就是要主动和客户搭话,要会适度的推销自己,让客户看到自己的能力。很多客户都是要有案例,其实有案例的无非就是考虑经验和成本。在没有案例的情况下,你得让他们相信你的能力或者低成本去完成,欺骗客户说有案例不是明智之举。
所谓“常联系”,很简单,太多的开发商会跟客户去联系,加微信,客户很可能漏掉回复或者厌烦了回复,谁能撑到最后谁就是胜利者,等只有你一个开发商跟客户联系的时候,你就赢了。
最后“快梳理”,快字很重要,能看出一个技术团队的能力,以及对这个项目的重视程度,作为客户,很希望自己能被上帝一样供着。
开发时:总结为“三常”—常联系常汇报常记录。常联系还是一样,有一些没有预料到的需求不明问题肯定会在开发过程中遇到,这时候就非常考验项目经理的本事了,要特别注意讲话的技巧。加功能改需求对自己不利,不加不改又担心难以维护这个客户。常汇报就是要把开发的进度及时的反馈给自己的客户,毕竟隔着整个网络呢,大家都不知道对方在干什么,适时的汇报能让客户放心。常记录,在开发过程中遇到问题,要及时的记录下来,并在适当的时候给客户说明问题的严重性,双方一起探讨最佳的解决方案,如果你能在项目遇到疑问的时候,及时的给出建设性的意见,会让客户对你产生崇拜的感觉。
交付后:千万不要把交付当做最后一步,这是非常关键的一步,能不能有下一次的合作机会就全在这个时候了。隔三差五的要询问客户有没有问题,遇到问题要及时帮助修复。所以还是常联系!和客户保持朋友一样的关系,下一单会来的特别快。
人工智能
2019-02-21 16:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
近些年来,人工智能领域又活跃起来,除了传统了学术圈外,Google、Microsoft、facebook等工业界优秀企业也纷纷成立相关研究团队,并取得了很多令人瞩目的成果。这要归功于社交网络用户产生的大量数据,这些数据大都是原始数据,需要被进一步分析处理;还要归功于廉价而又强大的计算资源的出现,比如GPGPU的快速发展。
除去这些因素,AI尤其是机器学习领域出现的一股新潮流很大程度上推动了这次复兴——深度学习。本文中我将介绍深度学习背后的关键概念及算法,从最简单的元素开始并以此为基础进行下一步构建。
机器学习基础
如果你不太熟悉相关知识,通常的机器学习过程如下:
1、机器学习算法需要输入少量标记好的样本,比如10张小狗的照片,其中1张标记为1(意为狗)其它的标记为0(意为不是狗)——本文主要使用监督式、二叉分类。
2、这些算法“学习”怎么样正确将狗的图片分类,然后再输入一个新的图片时,可以期望算法输出正确的图片标记(如输入一张小狗图片,输出1;否则输出0)。
这通常是难以置信的:你的数据可能是模糊的,标记也可能出错;或者你的数据是手写字母的图片,用其实际表示的字母来标记它。
感知机
感知机是最早的监督式训练算法,是神经网络构建的基础。
假如平面中存在 n 个点,并被分别标记为“0”和“1”。此时加入一个新的点,如果我们想知道这个点的标记是什么(和之前提到的小狗图片的辨别同理),我们要怎么做呢?
一种很简单的方法是查找离这个点最近的点是什么,然后返回和这个点一样的标记。而一种稍微“智能”的办法则是去找出平面上的一条线来将不同标记的数据点分开,并用这条线作为“分类器”来区分新数据点的标记。
在本例中,每一个输入数据都可以表示为一个向量 x = ( x_1, x_2 ) ,而我们的函数则是要实现“如果线以下,输出0;线以上,输出1”。
用数学方法表示,定义一个表示权重的向量 w 和一个垂直偏移量 b 。然后,我们将输入、权重和偏移结合可以得到如下传递函数:
这个传递函数的结果将被输入到一个激活函数中以产生标记。在上面的例子中,我们的激活函数是一个门限截止函数(即大于某个阈值后输出1):
训练
感知机的训练包括多训练样本的输入及计算每个样本的输出。在每一次计算以后,权重 w 都要调整以最小化输出误差,这个误差由输入样本的标记值与实际计算得出值的差得出。还有其它的误差计算方法,如均方差等,但基本的原则是一样的。
缺陷
这种简单的感知机有一个明显缺陷:只能学习线性可分函数。这个缺陷重要吗?比如 XOR,这么简单的函数,都不能被线性分类器分类(如下图所示,分隔两类点失败):
为了解决这个问题,我们要使用一种多层感知机,也就是——前馈神经网络:事实上,我们将要组合一群这样的感知机来创建出一个更强大的学习机器。
前馈神经网络
神经网络实际上就是将大量之前讲到的感知机进行组合,用不同的方法进行连接并作用在不同的激活函数上。
我们简单介绍下前向神经网络,其具有以下属性:
一个输入层,一个输出层,一个或多个隐含层。上图所示的神经网络中有一个三神经元的输入层、一个四神经元的隐含层、一个二神经元的输出层。
每一个神经元都是一个上文提到的感知机。
输入层的神经元作为隐含层的输入,同时隐含层的神经元也是输出层神经元的输入。
每条建立在神经元之间的连接都有一个权重 w (与感知机中提到的权重类似)。
在 t 层的每个神经元通常与前一层( t - 1层 )中的每个神经元都有连接(但你可以通过将这条连接的权重设为0来断开这条连接)。
为了处理输入数据,将输入向量赋到输入层中。在上例中,这个网络可以计算一个3维输入向量(由于只有3个输入层神经元)。假如输入向量是 [7, 1, 2],你将第一个输入神经元输入7,中间的输入1,第三个输入2。这些值将被传播到隐含层,通过加权传递函数传给每一个隐含层神经元(这就是前向传播),隐含层神经元再计算输出(激活函数)。
输出层和隐含层一样进行计算,输出层的计算结果就是整个神经网络的输出。
超线性
如果每一个感知机都只能使用一个线性激活函数会怎么样?整个网络的最终输出也仍然是将输入数据通过一些线性函数计算过一遍,只是用一些在网络中收集的不同权值调整了一下。换名话说,再多线性函数的组合还是线性函数。如果我们限定只能使用线性激活函数的话,前馈神经网络其实比一个感知机强大不到哪里去,无论网络有多少层。
正是这个原因,大多数神经网络都是使用的非线性激活函数,如对数函数、双曲正切函数、阶跃函数、整流函数等。不用这些非线性函数的神经网络只能学习输入数据的线性组合。
训练
大多数常见的应用在多层感知机的监督式训练的算法都是反向传播算法。基本的流程如下:
1、将训练样本通过神经网络进行前向传播计算。
2、计算输出误差,常用均方差:
其中 t 是目标值, y 是实际的神经网络计算输出。其它的误差计算方法也可以,但MSE(均方差)通常是一种较好的选择。
3、网络误差通过随机梯度下降的方法来最小化。
梯度下降很常用,但在神经网络中,输入参数是一个训练误差的曲线。每个权重的最佳值应该是误差曲线中的全局最小值(上图中的 global minimum )。在训练过程中,权重以非常小的步幅改变(在每个样本或每小组样本训练完成后)以找到全局最小值,但这可不容易,训练通常会结束在局部最小值上(上图中的local minima)。如例子中的,如果当前权重值为0.6,那么要向0.4方向移动。
这个图表示的是最简单的情况,误差只依赖于单个参数。但是,网络误差依赖于每一个网络权重,误差函数非常、非常复杂。
好消息是反向传播算法提供了一种通过利用输出误差来修正两个神经元之间权重的方法。关系本身十分复杂,但对于一个给定结点的权重修正按如下方法(简单):
其中 E 是输出误差, w_i 是输入 i 的权重。
实质上这么做的目的是利用权重 i 来修正梯度的方向。关键的地方在于误差的导数的使用,这可不一定好计算:你怎么样能给一个大型网络中随机一个结点中的随机一个权重求导数呢?
答案是:通过反向传播。误差的首次计算很简单(只要对预期值和实际值做差即可),然后通过一种巧妙的方法反向传回网络,让我们有效的在训练过程中修正权重并(期望)达到一个最小值。
隐含层
隐含层十分有趣。根据普适逼近原理,一个具有有限数目神经元的隐含层可以被训练成可逼近任意随机函数。换句话说,一层隐含层就强大到可以学习任何函数了。这说明我们在多隐含层(如深度网络)的实践中可以得到更好的结果。
隐含层存储了训练数据的内在抽象表示,和人类大脑(简化的类比)保存有对真实世界的抽象一样。接下来,我们将用各种方法来搞一下这个隐含层。
一个网络的例子
可以看一下这个通过 testMLPSigmoidBP 方法用Java实现的简单(4-2-3)前馈神经网络,它将 IRIS 数据集进行了分类。这个数据集中包含了三类鸢尾属植物,特征包括花萼长度,花瓣长度等等。每一类提供50个样本给这个神经网络训练。特征被赋给输入神经元,每一个输出神经元代表一类数据集(“1/0/0” 表示这个植物是Setosa,“0/1/0”表示 Versicolour,而“0/0/1”表示 Virginica)。分类的错误率是2/150(即每分类150个,错2个)。
大规模网络中的难题
神经网络中可以有多个隐含层:这样,在更高的隐含层里可以对其之前的隐含层构建新的抽象。而且像之前也提到的,这样可以更好的学习大规模网络。增加隐含层的层数通常会导致两个问题:
1、梯度消失:随着我们添加越来越多的隐含层,反向传播传递给较低层的信息会越来越少。实际上,由于信息向前反馈,不同层次间的梯度开始消失,对网络中权重的影响也会变小。
2、过度拟合:也许这是机器学习的核心难题。简要来说,过度拟合指的是对训练数据有着过于好的识别效果,这时导至模型非常复杂。这样的结果会导致对训练数据有非常好的识别较果,而对真实样本的识别效果非常差。
下面我们来看看一些深度学习的算法是如何面对这些难题的。
自编码器
大多数的机器学习入门课程都会让你放弃前馈神经网络。但是实际上这里面大有可为——请接着看。
自编码器就是一个典型的前馈神经网络,它的目标就是学习一种对数据集的压缩且分布式的表示方法(编码思想)。
从概念上讲,神经网络的目的是要训练去“重新建立”输入数据,好像输入和目标输出数据是一样的。换句话说:你正在让神经网络的输出与输入是同一样东西,只是经过了压缩。这还是不好理解,先来看一个例子。
压缩输入数据:灰度图像
这里有一个由28x28像素的灰度图像组成的训练集,且每一个像素的值都作为一个输入层神经元的输入(这时输入层就会有784个神经元)。输出层神经元要有相同的数目(784),且每一个输出神经元的输出值和输入图像的对应像素灰度值相同。
在这样的算法架构背后,神经网络学习到的实际上并不是一个训练数据到标记的“映射”,而是去学习数据本身的内在结构和特征(也正是因为这,隐含层也被称作特征探测器(feature detector))。通常隐含层中的神经元数目要比输入/输入层的少,这是为了使神经网络只去学习最重要的特征并实现特征的降维。
我们想在中间层用很少的结点去在概念层上学习数据、产生一个紧致的表示方法。
流行感冒
为了更好的描述自编码器,再看一个应用。
这次我们使用一个简单的数据集,其中包括一些感冒的症状。如果感兴趣,这个例子的源码发布在这里。
数据结构如下:
输入数据一共六个二进制位
前三位是病的证状。例如, 1 0 0 0 0 0 代表病人发烧; 0 1 0 0 0 0 代表咳嗽; 1 1 0 0 0 0 代表即咳嗽又发烧等等。
后三位表示抵抗能力,如果一个病人有这个,代表他/她不太可能患此病。例如, 0 0 0 1 0 0 代表病人接种过流感疫苗。一个可能的组合是: 0 1 0 1 0 0 ,这代表着一个接种过流感疫苗的咳嗽病人,等等。
当一个病人同时拥用前三位中的两位时,我们认为他生病了;如果至少拥用后三位中的两位,那么他是健康的,如:
111000, 101000, 110000, 011000, 011100 = 生病
000111, 001110, 000101, 000011, 000110 = 健康
我们来训练一个自编码器(使用反向传播),六个输入、六个输出神经元,而只有两个隐含神经元。
在经过几百次迭代以后,我们发现,每当一个“生病”的样本输入时,两个隐含层神经元中的一个(对于生病的样本总是这个)总是显示出更高的激活值。而如果输入一个“健康”样本时,另一个隐含层则会显示更高的激活值。
再看学习
本质上来说,这两个隐含神经元从数据集中学习到了流感症状的一种紧致表示方法。为了检验它是不是真的实现了学习,我们再看下过度拟合的问题。通过训练我们的神经网络学习到的是一个紧致的简单的,而不是一个高度复杂且对数据集过度拟合的表示方法。
某种程度上来讲,与其说在找一种简单的表示方法,我们更是在尝试从“感觉”上去学习数据。
受限波尔兹曼机
下一步来看下受限波尔兹曼机(Restricted Boltzmann machines RBM),一种可以在输入数据集上学习概率分布的生成随机神经网络。
RBM由隐含层、可见层、偏置层组成。和前馈神经网络不同,可见层和隐含层之间的连接是无方向性(值可以从可见层->隐含层或隐含层->可见层任意传输)且全连接的(每一个当前层的神经元与下一层的每个神经元都有连接——如果允许任意层的任意神经元连接到任意层去,我们就得到了一个波尔兹曼机(非受限的))。
标准的RBM中,隐含和可见层的神经元都是二态的(即神经元的激活值只能是服从伯努力分布的0或1),不过也存在其它非线性的变种。
虽然学者们已经研究RBM很长时间了,最近出现的对比差异无监督训练算法使这个领域复兴。
对比差异
单步对比差异算法原理:
1、正向过程:
输入样本 v 输入至输入层中。
v 通过一种与前馈网络相似的方法传播到隐含层中,隐含层的激活值为 h 。
2、反向过程:
将 h 传回可见层得到 v’ (可见层和隐含层的连接是无方向的,可以这样传)。
再将 v’ 传到隐含层中,得到 h’ 。
3、权重更新:
其中 a 是学习速率, v , v’ , h , h’ 和 w 都是向量。
算法的思想就是在正向过程中影响了网络的内部对于真实数据的表示。同时,反向过程中尝试通过这个被影响过的表示方法重建数据。主要目的是可以使生成的数据与原数据尽可能相似,这个差异影响了权重更新。
换句话说,这样的网络具有了感知对输入数据表示的程度的能力,而且尝试通过这个感知能力重建数据。如果重建出来的数据与原数据差异很大,那么进行调整并再次重建。
再看流行感冒的例子
为了说明对比差异,我们使用与上例相同的流感症状的数据集。测试网络是一个包含6个可见层神经元、2个隐含层神经元的RBM。我们用对比差异的方法对网络进行训练,将症状 v 赋到可见层中。在测试中,这些症状值被重新传到可见层;然后再被传到隐含层。隐含层的神经元表示健康/生病的状态,与自编码器相似。
在进行过几百次迭代后,我们得到了与自编码器相同的结果:输入一个生病样本,其中一个隐含层神经元具有更高激活值;输入健康的样本,则另一个神经元更兴奋。
例子的代码在这里。
深度网络
到现在为止,我们已经学习了隐含层中强大的特征探测器——自编码器和RBM,但现在还没有办法有效的去利用这些功能。实际上,上面所用到的这些数据集都是特定的。而我们要找到一些方法来间接的使用这些探测出的特征。
好消息是,已经发现这些结构可以通过栈式叠加来实现深度网络。这些网络可以通过贪心法的思想训练,每次训练一层,以克服之前提到在反向传播中梯度消失及过度拟合的问题。
这样的算法架构十分强大,可以产生很好的结果。如Google著名的“猫”识别,在实验中通过使用特定的深度自编码器,在无标记的图片库中学习到人和猫脸的识别。
下面我们将更深入。
栈式自编码器
和名字一样,这种网络由多个栈式结合的自编码器组成。
自编码器的隐含层 t 会作为 t + 1 层的输入层。第一个输入层就是整个网络的输入层。利用贪心法训练每一层的步骤如下:
1、通过反向传播的方法利用所有数据对第一层的自编码器进行训练( t=1 ,上图中的红色连接部分)。
2、训练第二层的自编码器 t=2 (绿色连接部分)。由于 t=2 的输入层是 t=1 的隐含层,我们已经不再关心 t=1 的输入层,可以从整个网络中移除。整个训练开始于将输入样本数据赋到 t=1 的输入层,通过前向传播至 t = 2 的输出层。下面t = 2的权重(输入->隐含和隐含->输出)使用反向传播的方法进行更新。t = 2的层和 t=1 的层一样,都要通过所有样本的训练。
3、对所有层重复步骤1-2(即移除前面自编码器的输出层,用另一个自编码器替代,再用反向传播进行训练)。
4、步骤1-3被称为预训练,这将网络里的权重值初始化至一个合适的位置。但是通过这个训练并没有得到一个输入数据到输出标记的映射。例如,一个网络的目标是被训练用来识别手写数字,经过这样的训练后还不能将最后的特征探测器的输出(即隐含层中最后的自编码器)对应到图片的标记上去。这样,一个通常的办法是在网络的最后一层(即蓝色连接部分)后面再加一个或多个全连接层。整个网络可以被看作是一个多层的感知机,并使用反向传播的方法进行训练(这步也被称为微调)。
栈式自编码器,提供了一种有效的预训练方法来初始化网络的权重,这样你得到了一个可以用来训练的复杂、多层的感知机。
深度信度网络
和自编码器一样,我也可以将波尔兹曼机进行栈式叠加来构建深度信度网络(DBN)。
在本例中,隐含层 RBM t 可以看作是 RBM t+1 的可见层。第一个RBM的输入层即是整个网络的输入层,层间贪心式的预训练的工作模式如下:
1. 通过对比差异法对所有训练样本训练第一个RBM t=1
2. 训练第二个RBM t=1 。由于 t=2 的可见层是 t=1 的隐含层,训练开始于将数据赋至 t=1 的可见层,通过前向传播的方法传至 t=1 的隐含层。然后作为 t=2 的对比差异训练的初始数据。
3. 对所有层重复前面的过程。
4. 和栈式自编码器一样,通过预训练后,网络可以通过连接到一个或多个层间全连接的 RBM 隐含层进行扩展。这构成了一个可以通过反向传僠进行微调的多层感知机。
本过程和栈式自编码器很相似,只是用RBM将自编码器进行替换,并用对比差异算法将反向传播进行替换。
(注: 例中的源码可以从 此处获得.)
卷积网络
这个是本文最后一个软件架构——卷积网络,一类特殊的对图像识别非常有效的前馈网络。
在我们深入看实际的卷积网络之臆,我们先定义一个图像滤波器,或者称为一个赋有相关权重的方阵。一个滤波器可以应用到整个图片上,通常可以应用多个滤波器。比如,你可以应用四个6x6的滤波器在一张图片上。然后,输出中坐标(1,1)的像素值就是输入图像左上角一个6x6区域的加权和,其它像素也是如此。
有了上面的基础,我们来介绍定义出卷积网络的属性:
卷积层 对输入数据应用若干滤波器。比如图像的第一卷积层使用4个6x6滤波器。对图像应用一个滤波器之后的得到的结果被称为特征图谱(feature map, FM),特征图谱的数目和滤波器的数目相等。如果前驱层也是一个卷积层,那么滤波器应用在FM上,相当于输入一个FM,输出另外一个FM。从直觉上来讲,如果将一个权重分布到整个图像上后,那么这个特征就和位置无关了,同时多个滤波器可以分别探测出不同的特征。
下采样层 缩减输入数据的规模。例如输入一个32x32的图像,并且通过一个2x2的下采样,那么可以得到一个16x16的输出图像,这意味着原图像上的四个像素合并成为输出图像中的一个像素。实现下采样的方法有很多种,最常见的是最大值合并、平均值合并以及随机合并。
最后一个下采样层(或卷积层)通常连接到一个或多个全连层,全连层的输出就是最终的输出。
训练过程通过改进的反向传播实现,将下采样层作为考虑的因素并基于所有值来更新卷积滤波器的权重。
可以在这看几个应用在 MNIST 数据集上的卷积网络的例子,在这还有一个用JavaScript实现的一个可视的类似网络。
实现
目前为止,我们已经学会了常见神经网络中最主要的元素了,但是我只写了很少的在实现过程中所遇到的挑战。
概括来讲,我的目标是实现一个深度学习的库,即一个基于神经网络且满足如下条件的框架:
一个可以表示多种模型的通用架构(比如所有上文提到的神经网络中的元素)
可以使用多种训练算法(反向传播,对比差异等等)。
体面的性能
为了满足这些要求,我在软件的设计中使用了分层的思想。
结构
我们从如下的基础部分开始:
NeuralNetworkImpl 是所有神经网络模型实现的基类。
每个网络都包含有一个 layer 的集合。
每一层中有一个 connections 的链表, connection 指的是两个层之间的连接,将整个网络构成一个有向无环图。
这个结构对于经典的反馈网络、RBM 及更复杂的如 ImageNet 都已经足够灵活。
这个结构也允许一个 layer 成为多个网络的元素。比如,在 Deep Belief Network(深度信度网络)中的layer也可以用在其 RBM 中。
另外,通过这个架构可以将DBN的预训练阶段显示为一个栈式RBM的列表,微调阶段显示为一个前馈网络,这些都非常直观而且程序实现的很好。
数据流
下个部分介绍网络中的数据流,一个两步过程:
定义出层间的序列。例如,为了得到一个多层感知机的结果,输入数据被赋到输入层(因此,这也是首先被计算的层),然后再将数据通过不同的方法流向输出层。为了在反向传播中更新权重,输出的误差通过广度优先的方法从输出层传回每一层。这部分通过 LayerOrderStrategy 进行实现,应用到了网络图结构的优势,使用了不同的图遍历方法。其中一些样例包含了 广度优先策略 和 定位到一个指定的层。层的序列实际上由层间的连接进行决定,所以策略部分都是返回一个连接的有序列表。
计算激活值。每一层都有一个关联的 ConnectionCalculator ,包含有连接的列表(从上一步得来)和输入值(从其它层得到)并计算得到结果的激活值。例如,在一个简单的S形前馈网络中,隐含层的 ConnectionCalculator 接受输入层和偏置层的值(分别为输入值和一个值全为1的数组)和神经元之间的权重值(如果是全连接层,权重值实际上以一个矩阵的形式存储在一个 FullyConnected 结构中,计算加权和,然后将结果传给S函数。 ConnectionCalculator 中实现了一些转移函数(如加权求和、卷积)和激活函数(如对应多层感知机的对数函数和双曲正切函数,对应RBM的二态函数)。其中的大部分都可以通过 Aparapi 在GPU上进行计算,可以利用迷你批次训练。
通过 Aparapi 进行 GPU 计算
像我之前提到的,神经网络在近些年复兴的一个重要原因是其训练的方法可以高度并行化,允许我们通过GPGPU高效的加速训练。本文中,我选择 Aparapi 库来进行GPU的支持。
Aparapi 在连接计算上强加了一些重要的限制:
只允许使用原始数据类型的一维数组(变量)。
在GPU上运行的程序只能调用 Aparapi Kernel 类本身的成员函数。
这样,大部分的数据(权重、输入和输出数据)都要保存在 Matrix 实例里面,其内部是一个一维浮点数组。所有Aparapi 连接计算都是使用 AparapiWeightedSum (应用在全连接层和加权求和函数上)、 AparapiSubsampling2D (应用在下采样层)或 AparapiConv2D (应用在卷积层)。这些限制可以通过 Heterogeneous System Architecture 里介绍的内容解决一些。而且Aparapi 允许相同的代码运行在CPU和GPU上。
训练
training 的模块实现了多种训练算法。这个模块依赖于上文提到的两个模块。比如, BackPropagationTrainer (所有的训练算法都以 Trainer 为基类)在前馈阶段使用前馈层计算,在误差传播和权重更新时使用特殊的广度优先层计算。
我最新的工作是在Java8环境下开发,其它一些更新的功能可以在这个branch 下获得,这部分的工作很快会merge到主干上。
结论
本文的目标是提供一个深度学习算法领域的一个简明介绍,由最基本的组成元素开始(感知机)并逐渐深入到多种当前流行且有效的架构上,比如受限波尔兹曼机。
神经网络的思想已经出现了很长时间,但是今天,你如果身处机器学习领域而不知道深度学习或其它相关知识是不应该的。不应该过度宣传,但不可否认随着GPGPU提供的计算能力、包括Geoffrey Hinton, Yoshua Bengio, Yann LeCun and Andrew Ng在内的研究学者们提出的高效算法,这个领域已经表现出了很大的希望。现在正是最佳的时间深入这些方面的学习。
人工智能
2019-02-21 14:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
为什么会谈神经网络?目前深度学习是最火的研究方向,而深度学习的基础就是神经网络。所以通过对神经网络的学习,可以掌握一门强大的机器学习的一种方式,能够做一些智能化的事情。
基础知识篇
由于要了解神经网络,需要对机器学习这门学科要有前置的一些认识,所以基础篇会讲解一部分关于机器学习相关的概念和流程。
什么是机器学习?
机器学习是人工智能的一个分支,通过机器学习算法从数据中自动分析获得规律,并利用规律对未知数据进行预测。
关于机器学习可以分为几种类型: 监督学习 无监督学习 半监督学习
有监督和无监督的学习差异在于:
有监督学习方法必须要有人工标记的数据集用于学习(根据数据集还需要划分为训练集/测试集/验证集),而非监督学习没有标记好的数据集,只有一组数据,在该组数据集内寻找规律(前者可以理解为利用现有数据分类,后者则为利用数据聚类)。半监督学习则是基于有监督和无监督进行组合的一种学习方式。
关于有监督学习涉及到的算法基本为回归算法和分类算法,举例如下: 线性回归 逻辑回归 朴素贝叶斯 决策树 神经网络 KNN SVM
关于无监督学习涉及到的算法基本为聚类算法,举例如下: k-means PCA GMM
所以总结下来,本文主要讲的范围就是有监督学习下的深度神经网络的学习,这也是目前用的过程最为广泛的机器学习方法。至于其他的有监督的学习算法和无监督的学习算法,读者可以自行找书籍学习了解。后续本文主要围绕神经网络来讲解。
关于有监督学习工程化相关概念
有监督学习的工程化就是需要人工标记的正确数据样本(一组特征对应的正确标签)通过模型的训练,对结果的评估计算,不断调整完善计算参数的过程。最终的结果就是希望模型评估出的结果能够逼近我们实际正确的标签。
这里介绍几个工程中基本的概念:
标签
标签是我们要预测的事物,即简单线性回归中的 y 变量。标签可以是小麦未来的价格、图片中显示的动物品种、音频剪辑的含义或任何事物。
特征
特征是输入变量,简单的机器学习项目可能会使用单个特征(特征必须具体不能太宽泛),而比较复杂的机器学习项目可能会使用数百万个特征,按如下方式指定:
模型
模型定义了特征与标签之间的关系。例如,垃圾邮件检测模型可能会将某些特征与“垃圾邮件”紧密联系起来。可以用简单的数学公式来理解模型: y=f(x) 这里的y为标签,x输入项为特征,而整个公式就是模型
一般来说模型的形成,需要进行训练和推断两个过程: 训练:通过有标签的样本,让模型学习特征跟标签的关系。 推断:通过训练后的模型来对无标签的样本进行推断,让模型对新的特征进行预测来得到有用的结果。
根据模型的用途,可分为两种用途的模型: 回归模型:回归模型可预测连续值。如加利福尼亚州一栋房产的价值是多少?用户点击此广告的概率是多少? 分类模型:分类模型可预测离散值。某个指定电子邮件是垃圾邮件还是非垃圾邮件?这是一张狗、猫还是仓鼠图片?
模型训练过程:损失评估和降低损失
模型的训练过程,同样的输入特征,模型训练输出的实际结果和正确数据样本集的实际结果必然会有一定的差异,这里怎么评估模型的实际结果和样本集中的正确结果之间的差异大小称为损失评估。这里会涉及到一些常用的损失函数:平方函数(L2损失)和均方误差(MSE)损失函数等。
评估出损失之后,就需要考虑怎么降低损失,从而对模型参数进行调整,这个过程称为降低损失(主要描述了损失跟模型参数的调整关系,通过一定的学习速率可以使得当损失最小)。常用的降低损失的方法为梯度下降法。梯度下降方法也有对应的一些函数。
关于梯度下降法
假如我们研究的是回归问题,所产生的损失与w1的图形始终是凸形。换言之,图形始终是碗状图,如下所示:
凸形问题只有一个最低点;即只存在一个斜率正好为 0 的位置。这个最小值就是损失函数收敛之处。通过计算整个数据集中每个可能值的损失函数来找到收敛点这种方法效率太低。我们可以通过梯度下降法,梯度下降法的第一个阶段是为w1选择一个起始值(起点)。起点并不重要;因此很多算法就直接将w1设为 0 或随机选择一个值。下图显示的是我们选择了一个稍大于 0 的起点:
然后,梯度下降法算法会计算损失曲线在起点处的梯度。简而言之,梯度是偏导数的矢量;它可以让您了解哪个方向距离目标“更近”或“更远”。请注意,损失相对于单个权重的梯度就等于导数。 请注意,梯度是一个矢量,因此具有以下两个特征: 方向 大小
梯度始终指向损失函数中增长最为迅猛的方向。梯度下降法算法会沿着负梯度的方向走一步,以便尽快降低损失。
为了确定损失函数曲线上的下一个点,梯度下降法算法会将梯度大小的一部分与起点相加,如下图所示:
然后,梯度下降法会重复此过程,逐渐接近最低点。
关于学习速率
梯度矢量具有方向和大小。梯度下降法算法用梯度乘以一个称为学习速率(有时也称为步长)的标量,以确定下一个点的位置。例如,如果梯度大小为 2.5,学习速率为 0.01,则梯度下降法算法会选择距离前一个点 0.025 的位置作为下一个点。我们会花费相当多的时间来调整学习速率。 如果您选择的学习速率过小,就会花费太长的学习时间:
如果您指定的学习速率过大,下一个点将永远在 U 形曲线的底部随意弹跳。
损失函数的梯度较小,则可以放心地试着采用更大的学习速率,以补偿较小的梯度并获得更大的步长。学习速率刚好的情况。
总结
总体来说有监督学习工程化主要就是通过正确的标签数据对应的特征进行输入,经过模型得到实际结果,再将实际结果和正确的标签结果进行损失评估得到一个损失大小的值,然后通过降低损失的算法来根据损失大小的值按一定的学习速率来调整模型参数的权重值,使得参数经过几轮训练后得到最优解(用数学的话讲:就是不断的拟合数据,得到最合适的函数)。最后得到的模型就可以用于进行预测或者是分类求解新的数据。
关于标签数据的一些问题(如何提高数据质量以及模型效果)
泛化数据:解决过拟合问题
过拟合模型在训练过程中产生的损失很低,但在预测新数据方面的表现却非常糟糕。当有新数据加入的时候,模型并不能很好地对数据进行预测。所以要经可能适当的拟合我们的数据。尽可能的泛化数据:
将数据划分成三份,分别为训练集/验证集/测试集。并按照如下流程:
这样可以达到以下效果: 训练集用于日常训练。 验证集上获得最佳效果的模型。 使用测试集再次检查该模型的有效性。
当然这里对于过拟合的问题还有涉及到的数据正则化,降低复杂模型的复杂度来防止过拟合,这种原则称为正则化。(有 L1和L2 正则化公式,具体可以自行谷歌了解)
特征处理:特征工程(怎么处理特征归类,规范化特征)
左侧表示来自输入数据源的原始数据,右侧表示特征矢量,也就是组成数据集中样本的浮点值集。 特征工程指的是将原始数据转换为特征矢量。进行特征工程预计需要大量时间,用于解决该类映射问题。 机器学习模型通常期望样本表示为实数矢量。这种矢量的构建方法如下:为每个字段衍生特征,然后将它们全部连接到一起。 数值映射:
机器学习模型根据浮点值进行训练,因此整数和浮点原始数据不需要特殊编码。
字符串映射:
模型无法通过字符串值学习规律,因此您需要进行一些特征工程来将这些值转换为数字形式。然后,使用该词汇表创建一个独热编码,用于将指定字符串值表示为二元矢量。在该矢量(与指定的字符串值对应)中,该矢量的长度等于词汇表中的元素数。
枚举映射:
分类特征具有一组离散的可能值。例如,名为 Lowland Countries 的特征只包含 3 个可能值: {'Netherlands', 'Belgium', 'Luxembourg'}
将分类特征(如 Lowland Countries)编码为枚举类型或表示不同值的整数离散集。例如: 将荷兰表示为 0 将比利时表示为 1 将卢森堡表示为 2 良好的特征特点: 避免很少使用的离散特征值(数据尽可能出现多次,避免使用很偏离数据集合的数据) 最好具有清晰明确的含义 排除异常的值(需要进行数据的清洗) 尽可能使用稳定的数据,不会随着时间变化 数据清理:由于少量的错误数据也会破坏整体数据集合,所以我们需要对数据做清理。 缩放特征值帮助梯度下降法更快速地收敛 处理极端离群值(处理离散数据) 分解特征,了解特征。以便于更好抽出具体的特征。(特征分箱/特征清查/特征组合)
总结
为什么要撸这么多机器学习概念相关的基础篇,主要是为了能够更好了解机器学习入门的一些老生长谈的词汇,机器学习方向是很广的,了解概念帮助写代码更轻松。本文后续准备往神经网络方向去讲,当然神经网络其实最合适的场景是用于解决图像的问题。相反,其他的类型的问题,可能就不太合适用神经网络去解决了,明确自己场景用各种机器学习方法进行组合才是最合适的。目前神经网络这块框架其实不少,类似tensorflow(提供了相对底层的接口)或者Keras(提供了相对高级的接口) 这类的深度学习框架。当然,其他的算法也有对应的框架(强化学习如openAI)或者是机器学习算法库,只不过这块要自己写算法而已,数学功底才是硬核。
人工智能
2019-02-20 23:44:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
读书笔记:
1. 乐嚼是丹麦的一种非常受欢迎的糖果,它的包装盒上有一种哲言慧语“小处诚实非小事”,代码也是一样,以小见大。
人工智能
2019-02-20 19:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
attention
以google神经机器翻译(NMT)为例 无attention:
encoder-decoder在无attention机制时,由encoder将输入序列转化为最后一层输出state向量,再由state向量来循环输出序列每个字符。
attention机制:
将整个序列的信息压缩在一维向量里造成信息丢失,并且考虑到输出的某个字符只与输入序列的某个或某几个相关,与其他输入字符不相关或相关性较弱,由此提出了attention机制。在encoder层将输入序列的每个字符output向量以不同权重进行组合再decode输出字符,每需要输出一个字符,encoder层权重序列都会变,这就可以理解为需要输出的字符是由哪些或那个字符影响最大,这就是注意力机制。
attention权重的获得需要通过一个函数层获得,而该函数参数需要通过training来优化,详细理解可以参考我这篇 blog
从增强字/词的语义表示这一角度来理解一下Attention机制:
Attention机制主要涉及到三个概念:Query、Key和Value。在上面增强字的语义表示这个应用场景中,目标字及其上下文的字都有各自的原始Value,Attention机制将目标字作为Query、其上下文的各个字作为Key,并将Query与各个Key的相似性作为权重,把上下文各个字的Value融入目标字的原始Value中。如下图所示,Attention机制将目标字和上下文各个字的语义向量表示作为输入,首先通过线性变换获得目标字的Query向量表示、上下文各个字的Key向量表示以及目标字与上下文各个字的原始Value表示,然后计算Query向量与各个Key向量的相似度作为权重,加权融合目标字的Value向量和各个上下文字的Value向量,作为Attention的输出,即:目标字的增强语义向量表示。
self-attention
self-attention来自于google文章《attention is all you need》。 一个序列每个字符对其上下文字符的影响作用都不同,每个字对序列的语义信息贡献也不同,可以通过一种机制将原输入序列中字符向量通过加权融合序列中所有字符的语义向量信息来产生新的向量,即增强了原语义信息。
Self-Attention:对于输入文本,我们需要对其中的每个字分别增强语义向量表示,因此,我们分别将每个字作为Query,加权融合文本中所有字的语义信息,得到各个字的增强语义向量,如下图所示。在这种情况下,Query、Key和Value的向量表示均来自于同一输入文本,因此,该Attention机制也叫Self-Attention。
Multi-head Self-Attention
为了增强Attention的多样性,文章作者进一步利用不同的Self-Attention模块获得文本中每个字在不同语义空间下的增强语义向量,并将每个字的多个增强语义向量进行线性组合,从而获得一个最终的与原始字向量长度相同的增强语义向量,如下图所示。
Transformer
在Multi-headSelf-Attention的基础上再添加一些“佐料”,就构成了大名鼎鼎的Transformer Encoder。实际上,Transformer模型还包含一个Decoder模块用于生成文本,但由于BERT模型中并未使用到Decoder模块,因此这里对其不作详述。下图展示了Transformer Encoder的内部结构,可以看到,Transformer Encoder在Multi-head Self-Attention之上又添加了三种关键操作: 残差连接(ResidualConnection):将模块的输入与输出直接相加,作为最后的输出。这种操作背后的一个基本考虑是:修改输入比重构整个输出更容易(“锦上添花”比“雪中送炭”容易多了!)。这样一来,可以使网络更容易训练。 Layer Normalization:对某一层神经网络节点作0均值1方差的标准化。 线性转换:对每个字的增强语义向量再做两次线性变换,以增强整个模型的表达能力。这里,变换后的向量与原向量保持长度相同。
可以看到,Transformer Encoder的输入和输出在形式上还是完全相同,因此,Transformer Encoder同样可以表示为将输入文本中各个字的语义向量转换为相同长度的增强语义向量的一个黑盒。
bert
把Transformer Encoder模块一层一层的堆叠起来就是大名鼎鼎的bert了
参考1: https://mp.weixin.qq.com/s/HOt11jG0DhhVuFWYhKNXtg 参考2: https://mp.weixin.qq.com/s/RLxWevVWHXgX-UcoxDS70w
人工智能
2019-02-20 16:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Data Lake Analytics 作为云上数据处理的枢纽,最近加入了对于PolarDB的支持, PolarDB 是阿里云自研的下一代关系型分布式云原生数据库,100%兼容MySQL,存储容量最高可达 100T,性能最高提升至 MySQL 的 6 倍。这篇教程带你玩转 DLA 的 PolarDB 支持。
创建数据库
在 DLA 里面创建一个底层映射到 PolarDB 的外表的语法如下: CREATE SCHEMA porlardb_test WITH DBPROPERTIES ( CATALOG = 'mysql', LOCATION = 'jdbc:mysql://pc-bp1dlebalabala.rwlb.rds.aliyuncs.com:3306/dla_test', USER = 'dla_test_1', PASSWORD = 'the-fake-password', VPC_ID = 'vpc-2zeij924vxd303kwifake', INSTANCE_ID = 'rm-2zer0vg58mfo5fake' );
跟普通的建表不同的是这里多了两个属性: VPC_ID 和 INSTANCE_ID 。 VPC_ID 是你的PolarDB所在VPC的ID, 如下图所示:
建表需要这两个额外信息是因为现在用户的数据库都是处于用户自己的VPC内部,默认情况下 DLA 是访问不了用户 VPC 里面的资源的,为了让DLA能够访问到用户PolarDB面的数据,我们需要利用阿里云的VPC反向访问技术。 权限声明: 当您通过上述方式建库,就视为您同意我们利用VPC反向访问的技术去读写您的PolarDB。
另外您还需要把 100.104.0.0/16 IP地址段加入到你的PolarDB的白名单列表,这是我们VPC反向访问的IP地段,如下图:
同时细心的读者可能注意到我们这里的 CATALOG 写的是 mysql , 而不是 polardb , 这是因为 PolarDB 100%兼容MySQL,我们直接以MySQL协议去访问就好了。
创建表
数据库建完之后,我们可以建表了,我们先在你的 PolarDB 里面建立如下的 person 表用来做测试: create table person ( id int, name varchar(1023), age int );
并且向里面插入一下测试数据: insert into person values (1, 'james', 10), (2, 'bond', 20), (3, 'jack', 30), (4, 'lucy', 40);
然后就可以在 DLA 的数据库里面建立相应的映射表了: create external table person ( id int, name varchar(1023), age int );
这样我们通过MySQL客户端连接到 DLA 数据库上面,就可以对 PolarDB 数据库里面的数据进行查询了: mysql> select * from person; +------+-------+------+ | id | name | age | +------+-------+------+ | 1 | james | 10 | | 2 | bond | 20 | | 3 | jack | 30 | | 4 | lucy | 40 | +------+-------+------+ 4 rows in set (0.35 sec)
总结
今天主要介绍了一下如果在DLA里面查询PolarDB的数据,因为PolarDB本身兼容MySQL协议,所以在DLA里面的使用上跟MySQL基本一样,因此这里的介绍比较简单,更多的内容就留给读者自己去探索了。
原文链接
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-02-20 15:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
2019年1月18日,由阿里巴巴 MaxCompute 开发者社区和阿里云栖社区联合主办的“阿里云栖开发者沙龙大数据技术专场”走近北京联合大学,本次技术沙龙上,阿里巴巴高级技术专家吴永明为大家分享了MaxCompute,基于Serverless的高可用大数据服务,以及MaxCompute低计算成本背后的秘密。
以下内容根据演讲视频以及PPT整理而成。
一、什么是 MaxCompute
Big Data in Alibaba
首先为大家介绍阿里巴巴大数据技术的一些相关背景。正如下图所示,阿里巴巴其实在很早的时候就开始布局大数据技术,甚至可以说阿里云的成立就是为了帮助阿里巴巴解决大数据相关的技术问题。如今,阿里巴巴几乎所有的BU都在使用大数据技术。而在阿里巴巴内部,大数据技术不仅应用范围非常广,同时也非常深入。此外,整个集团的大数据体系最终都会汇集到一起。

Overview of Computing Platform
阿里云计算平台事业部的职责就是将阿里巴巴的大数据体系汇集到一起,并且负责整个集团的存储、计算相关的研发工作。如下图所示的就是阿里巴巴大数据平台的简单架构图,最底层的是统一存储平台盘古,其负责存储大数据。存储时静态的,而为了挖掘数据的价值就需要依靠计算能力实现,因此阿里巴巴大数据平台也提供了各种计算资源,比如CPU、GPU、FPGA以及ASIC等。为了更好地利用上述这些计算资源,需要将他们统一地进行抽象,并有效地进行管理,而在阿里云内部,统一的资源管理系统称为伏羲。基于资源管理和调度系统,阿里云还开发了各种各样的计算引擎,比如通用计算引擎MaxCompute、流计算Blink、机器学习PAI以及图计算平台Flash等。在这些计算引擎之上,平台还提供了各种各样的开发环境,并基于这些开发环境实现了各种业务。

本文中将为大家重点介绍通用计算引擎MaxCompute。MaxCompute是一个通用分布式大数据处理平台,其一方面可以存储海量数据,另一方面还可以基于数据进行通用计算。
首先看两个数字,目前,阿里巴巴内部99%的数据存储和95%的计算任务都由MaxCompute承载。其实,MaxCompute就是阿里巴巴的数据中台,集团的各个BU所产生的数据最终都会汇总到MaxCompute上面,使得集团的数据资产得以不断增加。其次,再看几个指标。MaxCompute在BigBench测试中的表现是一般开源系统的2.5倍。在阿里巴巴集团内部,MaxCompute集群的机器数量达到了几万台,每天所承载的作业量更是达到了百万级别。目前,MaxCompute集群所存储的数据量也非常大在很久之前就已经达到了EB级别,这在全球范围内都处于领先水平。此外,MaxCompute不仅对集团内部提供服务,也开放给了外部企业使用,目前所提供的行业解决方案已经达到了50多套。如今,MaxCompute的存储量和计算量每年都在以极高的速度增长,而借助阿里云的能力,使得MaxCompute不仅能够在国内进行部署,还能够部署到海外的很多国家和地区。
MaxCompute系统架构
MaxCompute的系统架构与业界的大数据计算引擎架构比较类似。如下图所示,用户能够通过一些客户端进行接入,通过接入层就可以将数据传输到MaxCompute里面。而在中间存在一个管理层,可以管理各种各样的用户作业,同时也会承载各种各样的功能,比如大数据里面最基本的SQL编译、优化和运行等功能。此外,MaxCompute提供了分布式元数据服务,因为如果没有元数据管理,那么就无法知道数据存储的究竟是什么。在MaxCompute架构的最下面一层就是实际的存储和计算发生的地方。

二、MaxCompute Serverless与背后的奥秘
如今Serverless这一概念非常火爆,而当站在MaxCompute开发者的角度,Serverless的火爆既令人高兴,也令人郁闷。值得高兴的是:MaxCompute技术团队早在Serverless概念提出之前的很多年就开始研发类似功能了,并且设计理念也完全符合Serverless,这可以说明MaxCompute在Serverless方面的布局较早,并且技术具有一定的先进性。但令人郁闷的是:虽然MaxCompute团队很早就被提出了Serverless的相关概念,也认可这种技术路线的价值,但是却没有能够及早地将这样的能力包装起来。

在Serverless概念提出之前,大家进行大数据实践的时候基本都是按照下图的步骤,首先购买服务器,搭建集群,有了硬件之后就可以在上面安装大数据处理软件“全家桶”,之后导入数据,编写业务所需要的查询并做计算,最终获取结果。当然了,上述的每个步骤都离不开运维相关工作。如果自行搭建大数据系统,那么运维工作将会贯穿始终的,而对于运维同学而言,也需要时时刻刻待命,以防系统出现问题。
但是,如果回顾上述的步骤,真正能够产生业务价值就是第四步的编写查询和进行实际的计算,但是其他的步骤会消耗很多的资源和人力,而这对于偏向于业务型的公司而言,都是额外的负担。而如今,Serverless就提供了帮助企业消除这些额外负担的机会。
以MaxCompute Serverless为例,只需要经过四个步骤就可以实现所需的业务价值。第一步开通账户并创建项目,这个项目既是数据存储载体,也是计算的载体;第二步上传数据;第三步编写查询语句并进行计算;第四步就可以获取计算结果。在整个过程中,无需任何运维工作,因此开发团队可以将精力集中在最为核心并且能够产生业务价值的地方,这也就是Serverless的优势所在。其实,这也代表了新技术对日常工作所带来的变化,新的技术使得大家能够更加专注于能够产生核心价值的部分,而无需关心额外的或者辅助性的工作。
和其他的开源软件或者大数据公司直接为客户提供一个大数据软件包的做法不同,MaxCompute所提供的是大数据服务,而且所提供的服务能够实现365(天)X 24(小时)的高可靠,既提供了大数据计算的能力,也同时提供了数据存储的能力,并且使得用户可以真正实现大数据技术的开箱即用。
在实现大数据平台Serverless服务的过程中需要面对很多挑战,在本文中将会主要聚焦以下三个问题: 大数据服务如何持续迭代和升级 大数据服务的趋势:自动化、智能化 数据安全
持续改进和发布中的挑战和方案
如果想要实现一套大数据系统,那么持续改进和发布的话题是绝对无法脱离。这是因为用户总会提出各种各样的新需求,而为了满足这些需求就需要不断地升级和改进大数据平台,而在改进的同时,还需要面对每天平台之上运行的百万级作业,并保证7*24小时的服务不间断。那么,如何实现平台升级过程的平稳安全,并且使得用户对于新功能发布无感知,此外还需要保证新版本的稳定性,没有Bug和性能回退问题,以及在出现问题之后能够快速止损等,这些都是需要考虑的问题。此外,在持续改进和发布的过程中,还需要考虑测试和数据安全性之间的矛盾。总而言之,在大数据平台之上实现持续改进和发布,就如同在飞行的飞机上面更换引擎。

面对上述的问题和挑战,阿里云MaxCompute团队做了大量的工作。目前,在大数据领域,MapReduce对于用户不够友好已经成为了业界的共识,类SQL的大数据引擎已经成为了主流。而对于类SQL的处理而言,最关键的就是三个步骤:编译、优化和执行。因此,MaxCompute也抓住了问题的关键,针对编译和优化开发了Playback工具,针对执行则开发了Flighting工具。除了上述比较偏向于测试验证阶段所使用的两种工具之外,在真正上线的时候,MaxCompute还提供了灰度上线等工具。
Playback工具
Playback诞生的背景就是MaxCompute需要快速地提高编译器和优化器的表达能力和性能优化水平。而在对编译器和优化器进行了重大改动之后,如何保证编译器和优化器没有问题就成为了一个挑战。最为原始的做法就是通过将一些已有的SQL放到编译器和优化器上执行,并通过执行结果的人工分析来发现编译器和优化器的改进中存在的问题。但是通过人工方式进行分析,可能需要一百年才能完成,这显然是无法容忍的。因此,MaxCompute团队希望借助大数据计算平台的运算能力来自我验证新的编译优化器。

其原理就是利用MaxCompute本身强大且灵活的能力,把所有真实的用户查询收集起来并放到MaxCompute系统里面,将用户查询以大规模、分布式的方式运行起来,并通过新版的编译优化器来完成这些作业的编译和优化。而由于MaxCompute采用了业内通行的模式,因此能够很容易地将作业插件插入到任务之中,进而对编译优化任务进行校验,如果存在问题也能够很容易地暴露出来。

总结而言,Playback一方面利用MaxCompute灵活且强大的计算能力来分析海量用户任务,另外一方面还借助对于UDF的支持和良好的隔离方案来进行编译优化和验证。这就使得对于新版本编译优化器的验证变得非常容易,对于之前想要实现但是却无能为力的任务,如今借助于大数据平台的强大能力得以实现。
Flighting工具
Flighting是针对于运行时的工具。其实,对于改进的优化器进行测试优化的常规做法就是搭建一个测试集群进行测试,这是最常规并且最自然的做法,但是也会造成极大的资源浪费,因为测试集群的成本非常大。由于测试环境与真实环境的任务复负载情况不同,因此,测试集群也无法真实地模拟出实际场景。此外,为了在测试环境中进行验证,需要测试人员来创造出一些数据,但是这些被创造出来的数据可能过于简单。否则就需要从真实场景中拿一些数据,但是这样做不仅需要经过用户的同意,还需要进行数据脱敏,这也是一件极其麻烦的事情。不仅可能产生数据泄露的风险,而且最终数据和真实数据也会存在一定的差异,很多情况无法验证出新功能所存在的问题。

而MaxCompute的运行时验证工具Flighting所使用的就是线上真实的集群和环境。借助于资源隔离的能力,使得MaxCompute集群中99%的计算资源交给用户来运行作业,而剩下的1%的资源交给Flighting工具进行执行器的新功能验证测试。这样的做法非常高效,不仅不需要拖取数据,还可以在生产环境中直接运行,可以容易地暴露出真实执行器所存在的问题。
灰度上线
MaxCompute平台具有灰度上线的能力,可以将任务按照重要性进行分级,进行细粒度发布,并且支持瞬时回滚,这样就可以将风险降到最低。
MaxCompute平台的持续开发验证迭代流程
如下图所示的是MaxCompute平台的持续开发验证迭代流程,其结合了针对编译优化器的Playback工具、针对运行器的Flighting工具以及灰度上线的能力。在这样的流程中, 第二个周期并不需要等待第一个周期完成再开始,这样就使得整个研发流程更加高效。目前,MaxCompute的持续开发和交付过程在编译优化、执行以及灰度等都具有较强的技术优势,在整个行业内也具有较强的技术竞争力,这就使得MaxCompute能够更好地向前演进,为客户带来更多更好的功能,同时也保证了MaxCompute平台在升级的过程中不会影响用户的正常使用。
自动化、智能化
随着人工智能和大数据技术的不断发展,如今对于服务而言,不仅需要实现高可用,还需要实现自动化。针对于这样的思想,可以从两个角度来看,即服务本身的角度和用户运维的角度。针对于服务本身,首先需要实现自动化,更进一步可以实现人工智能化。
任何服务都会出现各种各样的问题,存在各种各样的Bug,在出现问题的时候,传统解决方案中需要依靠人力来解决。而服务自动化和智能化的终极目标就是在系统出现问题的时候,不需要人工来修复,而完全通过系统的自愈能力来发现并解决问题。而服务自动化和智能化的目标需要一步步实现,从用户运维的角度来看,就是从原本用户自身负责的运维工作,转交给完成大数据任务的云上大数据平台,比如阿里云MaxCompute,这时候就相当于将客户的运维工作转移给了MaxCompute团队。而第三步就是MaxCompute团队也希望从大数据平台繁琐的运维任务中解脱出来,实现服务的自动化和智能化运维,进一步释放MaxCompute团队的精力,将全部的精力投入到更加有价值的工作上。而实现大数据服务自动化和智能化的目的在于提高服务可靠性,解放生产力,通过做更有价值的事情来回馈用户。基本思想就是定时地收集和监控服务的各项指标,针对异常指标及时作出应对措施。
除了大数据服务的自动化和智能化之外,对于用户查询而言,自动识别出可优化的查询并提供优化建议也非常关键。大家所写的SQL查询未必是最高效的,可能存在一些更加有效的方式或者更好的技术,因此需要自动识别出用户查询中可优化的部分,并给出相应的优化建议。对于这部分而言,也是业内的发展方向,目前MaxCompute团队也正在进行相应的研究工作。
三、MaxCompute低成本的奥秘
低成本使得MaxCompute更加具有竞争力,而在低成本的背后,是MaxCompute发挥出技术红利使得成本确确实实地降低下来。为了使得大数据任务的成本更低,MaxCompute主要在计算、存储和调度这三个方面进行改进和提高。 在计算方面,MaxCompute优化了自身的计算引擎,能够提高性能,降低作业占用资源的数量,这两种做法都可以使得平台能够承载更多计算任务,每个任务要么能够减少计算时间,要么可以减少作业大小,最终降低计算任务所占用的资源,这样就能够使得更多的资源得以空出来,承载更多的计算任务。 在存储方面,也有各种各样的优化方式。比如优化压缩算法,使用比较先进的列式存储,对于数据存储进行分级,对于冷数据进行归档,对于没有价值的数据进行及时回收等。 在调度方面,为了降低成本进行优化方式主要包括集群内调度和跨集群全局调度。

对于集群内部调度而言,所需要解决的问题非常多。一方面,每时每刻都有很多的作业实例在等待调度,另外一方面还有很多资源等着运行作业。
集群内调度
那么如何将两者结合起来就显得非常关键,也非常考验调度能力。目前,MaxCompute大数据平台的调度决策频率达到了4000+/s,这里所作出的调度决策是将一定大小的作业放入到相应的资源中去,尽可能做到既能满足作业所需资源,又不能浪费资源。

在性能方面,需要支持增量式调度,使得资源能够得到很好地利用;此外,还提供了弹性配额能力,使得多用户在运行计算任务的时候可以实现削峰填谷。因为在真正使用大数据平台的时候,大家不可能同时提交任务,在绝大多数情况下往往会错峰提交任务,因此就会出现资源使用的峰和谷,而通过弹性配额能力可以尽量实现削峰填谷,使得资源能够充分利用。对于任务的最优化调度而言,则需要考虑延迟与吞吐之间的平衡。在负载均衡方面,需要规避一些热点。此外,MaxCompute的调度决策系统还支持任务优先级和抢占,能够实现实时性和公平性。
此外,针对于复杂任务调度而言,还进一步划分了服务和作业的调度。这里所谓的任务就是一次性作业,而对于服务而言,在启动之后则会在一段时间之内一直不间断地处理数据作业。其实,服务和作业的调度存在着很大的差异,服务调度要求较高的可靠性,不能被打断,因为一旦被打断,重新恢复起来的成本将会非常高;而作业对于间断的容忍程度则要高一些,即使被打断,后续也可以拉起来继续运行。
二级配额组
对于单集群内的调度而言,重点介绍一下在一般的大数据平台中比较少见的二级配额组概念。在介绍二级配额组之前,首先介绍关于配额组的相关内容。简单而言,将物理集群的各种资源做成资源池之后,将一些资源整合到一个组里面就称之为配额组。在大数据平台上,作业就运行在配额组里面,而同一个配额组中的作业将会共享该配额组中的所有资源。而常见的一级配额组策略,存在着很多的问题。举例而言,如果一级配额组具有1000个CPU内核,而有100个用户在共同使用该配额组,某一时刻某一用户提交了一个非常大的作业,于是占满了整个配额组的1000个CPU内核,进而导致其他用户的作业全部需要等待。

为了解决上述情况中所出现的问题,就需要使用二级配额组。二级配额组在一级配额组的基础之上进行了进一步细分,设定了每个用户所能够使用的资源上限。每个一级配额组可以分为多个二级配额组,这样做的目的就是为了更好地利用资源。在二级配额组中,资源可以实现共享,但是即便某个用户需要使用的资源很多,也无法超过该二级配额组资源上限,因此可以比较好地保证用户体验。
与一般的大数据系统不同,MaxCompute除了能够实现单集群内部调度之外,还可以实现多集群之间跨集群的全局调度,这也是MaxCompute强大能力的体现。单个集群可能会受制于各种因素而无法实现扩容,因此需要实现集群级别的水平扩展。
对于业务量巨大的应用而言,为了保证当单个集群发生宕机的时候业务依旧能够正常运行,所以需要让业务在多个集群上运行,来保证保证业务的高可用。这时候就需要全局的跨集群调度能力,当一个作业进来之后,需要分析该作业可以向哪些集群上进行调度,以及目前哪些集群可供使用并且比较空闲。而此时还会涉及数据多版本的问题,因为同一份数据存储在不同的集群之中,就会带来数据不一致的问题。此外,对于同一份数据的不同版本,也需要考虑应该具体的计算和数据复制策略,而阿里云MaxCompute也是经过了大量经验的积累才使得上述策略更加完善。
四、数据安全
对于一个企业而言,使用大数据计算平台时最为关心的就是数据安全问题。从阿里巴巴的经验来看,在数据安全方面,用户最为担忧的主要有三点,第一点:将数据放在平台之上,其他人能否看到;第二点:将数据放到平台或者服务之上,提供服务的人是否会看到数据。第三点:将数据托管到大数据平台之上,如果平台出现了问题,那么数据怎么办。

对于用户的这些忧虑,MaxCompute都能比较完善地进行处理,用户基本上无需担心数据的安全性。首先,MaxCompute平台具有完善的鉴权和授权机制,数据是属于用户的,而不是平台的,虽然平台帮助用户进行存储和计算,但是却不具有数据所有权,无论是对数据进行访问还是计算都需要鉴权和授权。无论是其他用户还是平台方,在没有授权的情况下,都无法看到数据。此外,授权的维度也比较多样,包括表级别、列级别和作业级别。而因为MaxCompute是云上大数据平台,天然地具备多租户的特点,因此需要实现各个租户之间的数据和计算隔离。对于数据而言,MaxCompute平台实现了物理隔离,从根本上消除了数据的安全性问题。此外,平台还提供了严格的权限控制策略,用户无法查看其他用户的数据。此外,MaxCompute还提供了E2E全链路存储加密,这对于金融机构显得尤为重要;还提供了CRC校验,保证了数据正确性,而且这部分能力已经在阿里巴巴内部使用了多年,已经非常完善;最后MaxCompute平台还为用户提供了数据脱敏等功能,方便用户使用。
原文链接
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-02-20 15:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
自 2012 年 AlexNet 大展神威以来,研究者已经提出了各种卷积架构,包括 VGG、NiN、Inception、ResNet、DenseNet 和 NASNet 等,我们会发现模型的准确率正稳定提升。
但是现在这些提升并不仅仅来源于架构的修正,还来源于训练过程的改进:包括损失函数的优化、数据预处理方法的提炼和最优化方法的提升等。在过去几年中,卷积网络与图像分割出现大量的改进,但大多数在文献中只作为实现细节而简要提及,而其它还有一些技巧甚至只能在源代码中找到。
在这篇论文中,李沐等研究者研究了一系列训练过程和模型架构的改进方法。这些方法都能提升模型的准确率,且几乎不增加任何计算复杂度。它们大多数都是次要的「技巧」,例如修正卷积步幅大小或调整学习率策略等。总的来说,采用这些技巧会产生很大的不同。因此研究者希望在多个神经网络架构和数据集上评估它们,并研究它们对最终模型准确率的影响。
研究者的实验表明,一些技巧可以显著提升准确率,且将它们组合在一起能进一步提升模型的准确率。研究者还对比了基线 ResNet 、加了各种技巧的 ResNet、以及其它相关的神经网络,下表 1 展示了所有的准确率对比。这些技巧将 ResNet50 的 Top-1 验证准确率从 75.3%提高到 79.29%,还优于其他更新和改进的网络架构。此外,研究者还表示这些技巧很多都可以迁移到其它领域和数据集,例如目标检测和语义分割等。
论文:Bag of Tricks for Image Classification with Convolutional Neural Networks
论文地址:https://arxiv.org/pdf/1812.01187.pdf
摘要:图像分类研究近期的多数进展都可以归功于训练过程的调整,例如数据增强和优化方法的变化。然而,在这些文献中,大多数微调方法要么被简单地作为实现细节,或仅能在源代码中看到。在本文中,我们将测试一系列的微调方法,并通过控制变量实验评估它们对最终准确率的影响。我们将展示通过组合不同的微调方法,我们可以显著地改善多种 CNN 模型。例如,我们将 ImageNet 上训练的 ResNet-50 的 top-1 验证准确率从 75.3% 提升到 79.29。本研究还表明,图像分类准确率的提高可以在其他应用领域(如目标检测和语义分割)中实现更好的迁移学习性能。
2 训练过程
目前我们基本上都用小批量 SGD 或其变体训练神经网络,Algorithm 1 展示了 SGD 的模版过程(感兴趣的读者可以查阅原论文)。利用广泛使用的 ResNet 实现作为我们的基线,训练过程主要分为以下六个步骤:
随机采样一张图片,并解码为 32 位的原始像素浮点值,每一个像素值的取值范围为 [0, 255]。
随机以 [3/4, 4/3] 为长宽比、[8%, 100%] 为比例裁减矩形区域,然后再缩放为 224*224 的方图。
以 0.5 的概率随机水平翻转图像。
从均匀分布 [0.6, 1.4] 中抽取系数,并用于缩放色调和明亮度等。
从正态分布 N (0, 0.1) 中采样一个系数,以添加 PCA 噪声。
图像分别通过减去(123.68, 116.779, 103.939),并除以(58.393, 57.12, 57.375)而获得经归一化的 RGB 三通道。
经过六步后就可以训练并验证了,以下展示了基线模型的准确率:
表 2:文献中实现的验证准确率与我们基线模型的验证准确率,注意 Inception V3 的输入图像大小是 299*299。
3 高效训练
随着 GPU 等硬件的流行,很多与性能相关的权衡取舍或最优选择都已经发生了改变。在这一章节中,我们研究了能利用低精度和大批量训练优势的多种技术,它们都不会损害模型的准确率,甚至有一些技术还能同时提升准确率与训练速度。
3.1 大批量训练
对于凸优化问题,随着批量的增加,收敛速度会降低。人们已经知道神经网络会有类似的实证结果 [25]。换句话说,对于相同数量的 epoch,大批量训练的模型与使用较小批量训练的模型相比,验证准确率会降低。因此有很多方法与技巧都旨在解决这个问题:
线性扩展学习率:较大的批量会减少梯度的噪声,从而可以增加学习率来加快收敛。
学习率预热:在预热这一启发式方法中,我们在最初使用较小的学习率,然后在训练过程变得稳定时换回初始学习率。
Zero γ:注意 ResNet 块的最后一层可以是批归一化层(BN)。在 zero γ启发式方法中,我们对所有残差块末端的 BN 层初始化γ=0。因此,所有的残差块仅返回输入值,这相当于网络拥有更少的层,在初始阶段更容易训练。
无偏衰减:无偏衰减启发式方法仅应用权重衰减到卷积层和全连接层的权重,其它如 BN 中的γ和β都不进行衰减。
表 4:ResNet-50 上每种有效训练启发式的准确率效果。
3.2 低精度训练
然而,新硬件可能具有增强的算术逻辑单元以用于较低精度的数据类型。尽管具备性能优势,但是精度降低具有较窄的取值范围,因此有可能出现超出范围而扰乱训练进度的情况。
表 3:ResNet-50 在基线(BS = 256 与 FP32)和更高效硬件设置(BS = 1024 与 FP16)之间的训练时间和验证准确率的比较。
4 模型变体
我们将简要介绍 ResNet 架构,特别是与模型变体调整相关的模块。ResNet 网络由一个输入主干、四个后续阶段和一个最终输出层组成,如图 1 所示。输入主干有一个 7×7 卷积,输出通道有 64 个,步幅为 2,接着是 3 ×3 最大池化层,步幅为 2。输入主干(input stem)将输入宽度和高度减小 4 倍,并将其通道尺寸增加到 64。
从阶段 2 开始,每个阶段从下采样块开始,然后是几个残差块。在下采样块中,存在路径 A 和路径 B。路径 A 具有三个卷积,其卷积核大小分别为 1×1、3×3 和 1×1。第一个卷积的步幅为 2,以将输入长度和宽度减半,最后一个卷积的输出通道比前两个大 4 倍,称为瓶颈结构。路径 B 使用步长为 2 的 1×1 卷积将输入形状变换为路径 A 的输出形状,因此我们可以对两个路径的输出求和以获得下采样块的输出。残差块类似于下采样块,除了仅使用步幅为 1 的卷积。
我们可以改变每个阶段中残差块的数量以获得不同的 ResNet 模型,例如 ResNet-50 和 ResNet-152,其中的数字表示网络中卷积层的数量。
图 1:ResNet-50 的架构。图中说明了卷积层的卷积核大小、输出通道大小和步幅大小(默认值为 1),池化层也类似。
图 2:三个 ResNet 变体。ResNet-B 修改 ResNet 的下采样模块。ResNet-C 进一步修改输入主干。在此基础上,ResNet-D 再次修改了下采样块。
表 5:将 ResNet-50 与三种模型变体进行模型大小(参数数量)、FLOPs 和 ImageNet 验证准确率(top-1、top-5)的比较。
5 训练方法改进
5.1 余弦学习率衰减
Loshchilov 等人 [18] 提出余弦退火策略,其简化版本是按照余弦函数将学习速率从初始值降低到 0。假设批次总数为 T(忽略预热阶段),然后在批次 t,学习率η_t 计算如下:
其中η是初始学习率,我们将此方案称为「余弦」衰减。
图 3:可视化带有预热方案的学习率变化。顶部:批量大小为 1024 的余弦衰减和按迭代步衰减方案。底部:关于两个方案的 top-1 验证准确率曲线。
5.2 标签平滑
标签平滑的想法首先被提出用于训练 Inception-v2 [26]。它将真实概率的构造改成:
其中ε是一个小常数,K 是标签总数量。
图 4:ImageNet 上标签平滑效果的可视化。顶部:当增加ε时,目标类别与其它类别之间的理论差距减小。下图:最大预测与其它类别平均值之间差距的经验分布。很明显,通过标签平滑,分布中心处于理论值并具有较少的极端值。
5.3 知识蒸馏
在知识蒸馏 [10] 中,我们使用教师模型来帮助训练当前模型(被称为学生模型)。教师模型通常是具有更高准确率的预训练模型,因此通过模仿,学生模型能够在保持模型复杂性相同的同时提高其自身的准确率。一个例子是使用 ResNet-152 作为教师模型来帮助训练 ResNet-50。
5.4 混合训练
在混合训练(mixup)中,每次我们随机抽样两个样本 (x_i,y_i) 和 (x_j,y_j)。然后我们通过这两个样本的加权线性插值构建一个新的样本:
其中 λ∈[0,1] 是从 Beta(α, α) 分布提取的随机数。在混合训练中,我们只使用新的样本 (x hat, y hat)。
5.5 实验结果
表 6:通过堆叠训练改进方法,得到的 ImageNet 验证准确率。基线模型为第 3 节所描述的。
6 迁移学习
6.1 目标检测
表 8:在 Pascal VOC 上评估各种预训练基础网络的 Faster-RCNN 性能。
6.2 语义分割
表 9:在 ADE20K 上评估各种基础网络的 FCN 性能。

点赞+关注有惊喜
人工智能
2019-02-20 14:04:00