Quantcast
Channel: 英特尔开发人员专区文章
Viewing all 583 articles
Browse latest View live

Caffe学习笔记 第二部分 - Windows* 下基于Intel核显加速的clCaffe的安装,配置与性能提升

$
0
0

作者:Gu, Jianjun

clCaffe编译与配置

Intel clCaffe (https://github.com/01org/caffe)是利用基于Intel Skylake及以后的处理器核显(即Gen9架构以上)做硬件加速的一个修改版。如果你当前机器是基于Nvdia显卡,请用NV cuda加速版本;如果你的显卡是AMD的,请check out 官方BVLC caffe的opencl分支。

clCaffe的编译

安装编译所需要的软件

配置Windows*的环境变量

为了避免等会CMAKE生成编译脚本的时候找不到一些依赖关系,有的没的路径都加一些,包括Cmake, Git, Ananconda以及Python的路径。(环境变量的配置同上篇文章的2.2部分 )

下载和编译Intel clCaffe工程

clCaffe的编译过程和caffe基本类似。不同的是clCaffe所需的caffe-builder的libraries目录是放在clCaffe自己目录的build目录下,同时额外多下载编译了一些支持openCL运行的开源项目。为了简单起见,这里参考了一个开源项目里的编译脚本https://github.com/liyuming1978/caffe_example/blob/master/install_scripts/Windows_install/build-clcaffe.cmd

首先先创建一个clcaffe-windows的目录,下面提供了一个简单的编译脚本build-clcaffe.cmd(为了简化clCaffe的编译过程,这里直接提供了完整的编译脚本,不再解释脚本里每一步的具体目的,有兴趣的开发者可以自己研究修改脚本来满足自己的需求),在clcaffe-windows目录下创建并且执行这个批处理脚本文件。这个脚本是根据我自己的环境 Win10+VS2015+Python3.6写的,如果你的开发环境跟我的不同,比如是python2.7或者3.5,需要按照脚本里的注释做相应的修改。

@echo off
@setlocal EnableDelayedExpansion

echo must install openclsdk,python(anaconda),git,cmake,vs 2015 for desktop

::设置python所在路径, 基于python3,
::如果编译环境是python2, 需要修改下面print()这句话,按照python2的语法格式修改
for /f "delims=" %%t in ('python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"') do set py_path_str=%%t

::下载并编译clCaffe所需要的一些依赖项目
cd %~sdp0
git clone https://github.com/dlfcn-win32/dlfcn-win32
cd dlfcn-win32
cmake -G "Visual Studio 14 2015 Win64" .
cmake --build . --config Release
cd %~sdp0
::git clone https://github.com/ptillet/isaac.git (isaac build wrong, please use intel isaac)
git clone https://github.com/intel/isaac.git
cd isaac
mkdir build
cd build
cmake -G "Visual Studio 14 2015 Win64" ..
cmake --build . --config Release

::下载clCaffe的项目
cd %~sdp0
git clone https://github.com/01org/caffe.git
cd caffe
git checkout inference-optimize
git pull
git clone https://github.com/viennacl/viennacl-dev.git

::下面这部分是拷贝自己编译的caffe-builder的libraries到clCaffe的目录里,同时编译时需要
::修改WindowsDownloadPrebuiltDependencies.cmake,注释掉对应的网络下载和解压缩代码
::防止WindowsDownloadPrebuiltDependencies.cmake脚本报找不到网上对应的caffe-builder包,
:: 如果编译环境是python2.7或者3.5, 可以注释掉下面这段代码, 编译脚本会自动从网上下载预编译好的依赖库
cd %~sdp0
cd caffe
mkdir build
cd .\build
mkdir libraries
cd ..
xcopy C:\work\caffe-builder-1.1.0\build_v140_x64\libraries .\build\libraries /s /h /c /y

::设置编译参数,开始编译clCaffe
cd %~sdp0
cd caffe
set BUILD_PYTHON=1
set BUILD_PYTHON_LAYER=1
set USE_INTEL_SPATIAL=1
set USE_GREENTEA=1
set USE_ISAAC=1
set RUN_TESTS=0
set RUN_INSTALL=1
set PYTHON_VERSION=3
call scripts\build_win.cmd

echo "clCaffe compile done"

注意事项:
编译过程中会报一次错,错误为找不到caffe/proto/caffe.pb.h

这个错误不是本机编译环境的问题,而是因为这个项目还不完善,在Windows*下编译项目的顺序有些问题。在Windows*下在编译pretune_convert.vcxproj的时候,这个caffe.pb.h还没有生成。

解决办法也很简单,直接再执行一遍build-clcaffe.cmd中下图的这部分脚本即可。

::设置编译参数,开始编译clCaffe
cd %~sdp0
cd caffe
set BUILD_PYTHON=1
set BUILD_PYTHON_LAYER=1
set USE_INTEL_SPATIAL=1
set USE_GREENTEA=1
set USE_ISAAC=1
set RUN_TESTS=0
set RUN_INSTALL=1
set PYTHON_VERSION=3
call scripts\build_win.cmd

最终编译结束了

接下来要把编译出的一些动态库拷贝到caffe\build目录下,具体请参考下面的编译脚本build-install.cmd。

if not exist "%~sdp0\caffe\build\install\" (
	echo do not find caffe build
)else (

	:: copy lib and include
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\install\bin
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\install\bin
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\install\python\caffe
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\install\python\caffe
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\tools\Release
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\tools\Release

	copy /y %~sdp0\caffe\build\libraries\lib\boost_python-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_system-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_thread-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_filesystem-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_regex-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_chrono-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_date_time-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_atomic-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\glog.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\gflags.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\libprotobuf.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffehdf5_hl.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffehdf5.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffezlib.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\lmdb.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\leveldb.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\snappy_static.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\libopenblas.dll.a %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_highgui310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_videoio310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_imgcodecs310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_imgproc310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_core310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\isaac\build\lib\Release\isaac.lib %~sdp0\caffe\build\install\lib

	copy /y %OPENCL_LIBRARIES% %~sdp0\caffe\build\install\lib
	copy /y %PYTHON_LIBRARY% %~sdp0\caffe\build\install\lib

	xcopy %~sdp0\caffe\build\libraries\include %~sdp0\caffe\build\install\include /s /h /c /y
	move /y %~sdp0\caffe\build\install\include\boost-1_61\boost %~sdp0\caffe\build\install\include\boost
	mkdir %~sdp0\caffe\build\install\include\viennacl
	xcopy %~sdp0\caffe\viennacl-dev\viennacl %~sdp0\caffe\build\install\include\viennacl /s /h /c /y
	mkdir  %~sdp0\caffe\build\install\include\CL
	xcopy %~sdp0\caffe\viennacl-dev\CL %~sdp0\caffe\build\install\include\CL /s /h /c /y
	mkdir  %~sdp0\caffe\build\install\include\3rdparty
	xcopy %~sdp0\caffe\include\3rdparty %~sdp0\caffe\build\install\include\3rdparty /s /h /c /y

	echo "copy done"
)

运行

运行一下clCaffe项目自带的examples里的00-classification的代码来验证一下clCaffe是否能够正常运行,我们也可以由此看到clCaffe和BVLC caffe的运行流程上的一些不同

首先在C盘的根目录下建一个clcaffe_cache的目录,然后在这个clcaffe_cache的目录下建一个viennacl的子目录。

同时在Windows*的环境变量里加入VIENNACL_CACHE_PATH = c:\clcaffe_cache\viennacl

接下来看看电脑上的可使用的GPU设备的ID号,等下运行caffe的时候需要告诉caffe用哪个GPU设备。

打开Windows* 命令行控制台应用(command console),进入clcaffe-windows\caffe\build\tools\Release目录,执行

C:\work\clcaffe-windows\caffe\build\tools\Release>caffe.exe device_query

看一下输出,

可以看到我的电脑上有2个GPU设备,
Device id:0 是Intel HD Graphics 630核显
Device id:1 是CPU模拟的GPU设备

接着针对00-classification用的caffe模型,我们先生成一下caffe运行这个model所需要cache文件。(如果事先不生产cache文件,这个cache也会在caffe第一次运行caffe model的时候自动生成,但是会导致程序第一次运行时的运行时间过长)
打开Windows* 命令行控制台应用(command console),进入clcaffe-windows\caffe\build\tools\Release目录,执行

C:\work\clcaffe-windows\caffe\build\tools\Release>caffe.exe time -gpu 0 -phase TEST --model ..\..\..\models\bvlc_reference_caffenet\deploy.prototxt
  • 这个models\bvlc_reference_caffenet\deploy.prototxt就是用到的模型文件

Caffe会基于这个models来评估一个运行这个模型最快的opencl算法,并且把评估结果写到Cache文件里去。

准备工作终于结束了,下面跑一下00-classification例子吧

打开anaconda的命令行,进入clcaffe\caffe的examples目录,运行jupyter notebook

在打开的notebook中打开caffe自带的例子 00-calssification.ipynb

这是用一个训练好的Caffe模型来预测动物图片的例子,图片默认是使用Caffe项目里examples\image\cat.jpg。

先修改第2步中caffe的路径,将路径指向clcaffe

再修改第4步中的caffe运行模式,注释掉set_mode_cpu(),加上set_device(0),set_mode_gpu(),caffe接下来会把模型放到device id为0的GPU设备上。根据前面的caffe device_query的输出,id 0为本机的核显。

一路Shift+Enter运行下去,看到第8步predicted输出 

predicted class is: 281

第9步输出

output label: n02123045 tabby, tabby cat

预测结果是猫,说明clcaffe已经正确编译而且能运行了。

大功告成。

使用clCaffe的一些注意事项

  • clCaffe只支持Gen9及以上的Intel核显,即Intel Skylake架构及以后的微处理器的核显。
  • 生成caffe cache时建议预先用无权重系数模型来生产cache,不要在caffe第一次运行你自己的代码时on-the-fly的生成cache,容易造成GPU运行出错。(这个bug正在修复中)
  • Clcaffe在创建基于GPU的net模型时,这个net会基于set_device([GPU Device ID])传进去的那个GPU device ID创建。所以接下来这个net模型无法通过set_device()来切换另一个GPU硬件,如果想切换到另一个GPU上运行,必须通过set_device([GPU Device ID])设定一个新GPU Device,再重新定义一个新Net模型。

  • Windows*下clCaffe对python支持不好,python程序在退出时会异常。在Linux下无此问题。所以建议正式代码用C++来调用caffe,同时C++接口可以定义fp16的caffe模型,获得更高的性能。

Intel clCaffe核显带来的性能提升

接下来在我的两台PC机上分别运行一下基于CPU的BVLC caffe和基于GPU加速的clCaffe,看看在日常的学习生产硬件平台上(台式机和笔记本)运行caffe,核显加速能带来多少性能的提升。测试方法为在默认的Windows*10系统且安装了常用的办公软件及开发软件的环境下(不关闭任何默认打开的后台服务),利用前面用到的caffe自带的例子 00-classification,在代码的第11步,测试net.forward()的运行时间(如下图所示)。我们基于这个测试时间来做一个简单的性能对比。

基于Intel Core i5-7440HQ移动处理器的性能测试

CPU信息

CPU版本net.forward()运行时间

GPU信息

GPU版本net.forward()运行时间

性能提升
1560ms/309ms = 5.05倍

基于Intel Core i7-6700 桌面处理器的性能测试

CPU信息

CPU版本net.forward()运行时间

GPU信息

GPU版本net.forward()运行时间

性能提升
1370ms/262ms = 5.23倍

两个测试平台数据对比分析

  • 在Caffe的CPU实现上,Caffe模型的预测时间取决于CPU的核心数量和主频率
    net.forward()时间对比
    1560ms/1370ms = 1.14倍
    CPU频率对比
    3.4GHz/2.8GHz=1.21倍 
  • 在Caffe的GPU实现上,Caffe模型的预测时间主要取决于Net模型的复杂度和GPU的主频。
    net.forward()时间对比
    309ms/262ms = 1.18倍
    GPU频率对比
    1.15GHz/1GHz=1.15倍
  • 相同硬件平台上CPU实现和GPU实现对比,GPU版本的处理速度领先于CPU版本5倍以上
    net.forward()时间对比
    平台1:1560ms/309ms = 5.05倍
    平台2:1370ms/262ms = 5.23倍

结论

在人工智能领域,利用Intel核显GPU做硬件加速,在Caffe上做图像预测(Inference)时可以带来比纯CPU版本Caffe高达5倍以上的性能提升。这种使用场景特别适合使用Intel的低端桌面处理器,移动处理器,以及凌动处理器平台的IOT设备、Edge设备及家庭电脑上,在这种低功耗、低CPU性能的情况下可以利用Intel集成GPU大大提高这些硬件平台的AI预测速度。

后记

本文介绍的clCaffe并没有获得其最佳性能。要想让clCaffe获得最佳性能,我们还需要对模型进行优化(clCaffe采用的是模型融合),并采用FP16来进行推理。

模型融合(Model Fusion)意思是说在神经网络内,一些层可以合并在一起计算。通常情况下,我们可以将BatchNorm,Scale,Relu层合并进入Conv层。模型融合的好处是降低了数据读取的次数,因为在推理过程中除了运算,数据读写也占用了大量的时间。

FP16也称之为半精度浮点,一般浮点数为4字节(FP32),FP16顾名思义为2字节,大部分现代GPU中设计了FP16的运算单元, 相对FP32可以获得1.3~2倍数的性能提升。在clCaffe中可以创建Half类型的网络, 当这样的网络加载FP32的模型时,内部会自动转换成FP16的模型进行计算,速度可以进一步提升(但是,目前只能使用c/c++代码才能创建FP16网络)。

有关clCaffe的模型融合以及FP16推理相关的内容,将会在下一篇博文介绍。你也可以在这里https://github.com/liyuming1978/caffe_example找到更多的如何更好使用clCaffe的相关信息。


使用 CPU 增强《Star Trek™:Bridge Crew》的虚拟现实沉浸感

$
0
0

标题: 使用 CPU 增强《Star Trek™:Bridge Crew》的虚拟现实沉浸感 概览: 在虚拟现实 (VR) 细分市场中,环境交互、增强的物理模拟和摧毁的结合为游戏锦上添花,延长了玩家在游戏中花费的时间,增强了他们对游戏的兴趣。本文将向您逐一介绍《Star Trek™:Bridge Crew*》中实施的所有 CPU 密集型特性,提供用于使用基础系统的指令。 正文:

查看 PDF [2MB]

简介

一般而言,CPU 通常不是影响游戏中震撼的沉浸式场景的主要因素。过去,少数游戏将设置呈现给玩家,允许他们调整 CPU 使用,但是许多开发人员认为针对不同的硬件等级实施基于 CPU 的多层系统有些得不偿失。通过与 Ubisoft 的 Red Storm Entertainment* 工作室的通力合作,《Star Trek™:Bridge Crew*》得到了明显改进,我们的目标是借助本文纠正该误解。在虚拟现实 (VR) 细分市场中,环境交互、增强的物理模拟和摧毁的结合为游戏锦上添花,延长了玩家在游戏中花费的时间,增强了他们对游戏的兴趣。鉴于 Oculus* 所需的低端硬件规格,清除根据全球最低规格定制 CPU 工作的想法变得越来越重要。使用可用的系统资源增强动态性和沉浸感将帮助您创建理想的游戏,同时允许尽可能多的玩家访问,我们进行了前所未有的简化。

本文将向您逐一介绍《Star Trek™:Bridge Crew*》中实施的所有 CPU 密集型特性,提供用于使用基础系统的指令。后面的章节简要介绍了对于每个性能层级,认定 CPU 工作过量的标准。最后一部分展示了如何轻松设置 CPU 性能类别,以自动检测最终用户的硬件等级。

《Star Trek™:Bridge Crew*》通过 Unity* 创建,后者将是本文的重点,但是所有概念也适用其他引擎。

请观看以下视频,查看关于运行这些效果的游戏的详细对比。

《Star Trek™:Bridge Crew*》中的 CPU 密集型特性

舰桥破坏 – 结合了物理粒子、刚体物理和实时全局照明 (GI)

Sparks

概述

USS Aegis 的舰桥是游戏的主要焦点之一。几乎所有游戏环节均要求玩家在舰桥中完成,因此,显然应在舰桥中应用绝大多数 CPU 任务,从而为玩家带来超值的体验。大部分时间均消耗在改进舰桥的各种摧毁顺序,将强度元素添加至场景。例如,舰桥被摧毁后,大块碎片飞出,火光在墙壁和地板上飞溅,火势蔓延,照亮了周围物体,并不是从光源直接照射。

什么原因使它成为 CPU 密集型任务?

破坏舰桥充分使用了 Unity 的实时刚体物理、带有大量小粒子的粒子系统(启用碰撞支持)以及各种火焰和火花效果创建的实时全局照明 (GI) 更新,它们可在可用 CPU 内核上扩展。生成了在破坏事件中使用刚体物理的各种碎片对象,激活高端效果后,粒子数量大幅提高。添加了面向粒子的全局碰撞支持和碰撞基元,内含用于创建火花跳跃和分散行为的详细信息。添加了其他使用 CPU 的 Unity 粒子特性,以增强场景,如使用子发射器为火球或火焰添加拖尾。舰桥破坏粒子在屏幕覆盖尺寸中保持较小的外形,以将 GPU 影响降至最低,同时实现预期的外观。破坏事件发生时,某些光源和放射面将闪烁以模拟电源中断。光源闪烁且舰桥上的火焰激活后,更新 GI。下面,我们将进入每个系统,了解如何单独使用它们。

概述

Unity* 的内置粒子系统组件支持外观和行为的多种变化。恰巧,内置粒子系统在可用的 CPU 内核之间出色扩展。用户只需轻点按钮,便可引发粒子系统碰撞以及与环境响应,如果您需要一个高度自定义的行为,可以编写每个粒子运动的脚本(稍后将详细介绍)。使用以下内置碰撞行为时,底层引擎将工作分配给可用内核,支持最大限度扩展系统。因此,您可以基于可用内核的数量扩展粒子数量,同时还需考虑处理器频率和高速缓存大小。只需转至相关的粒子系统组件,检查碰撞复选框,然后选择所需的设置,便可激活粒子碰撞。

碰撞设置组中包括若干选项。您需要考虑选择与世界碰撞,还是定义一系列平面,使粒子与平面碰撞。第一种设置将产生最真实的模拟,因为场景中几乎所有碰撞器都将运行每个粒子更新计算,但是这必然会导致 CPU 成本增加。游戏通常会定义一系列关键平面,用来模拟周围地形,以尽量减少计算,为其他 CPU 密集型效果预留空间。您的设置选择取决于游戏布局和您想取得的视觉效果。例如,以下命令定义 3 个平面为碰撞器:一层地面和两面墙。

ParticleSystem[] ParticleSystems;
    public Transform[] CollisionPlanes;

    public void Awake()
    {
        ParticleSystems = gameObject.GetComponentsInChildren();
        Debug.Log("Initializing ParticleSystemController");
    }

public void SetCPULevel(CPUCapabilityManager.SYSTEM_LEVELS sysLevel)
    {
        for (int i = 0; i < ParticleSystems.Length; i++)
        {
            var particleSysMain = ParticleSystems[i].main;
            var particleSysCollision = ParticleSystems[i].collision;
            var particleSysEmission = ParticleSystems[i].emission;
            if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.HIGH)
            {
                particleSysEmission.rateOverTime = 400.0f;
                particleSysMain.maxParticles = 20000;
                particleSysCollision.enabled = true;
                particleSysCollision.type = ParticleSystemCollisionType.World;
            }
            else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.MEDIUM)
            {
                particleSysEmission.rateOverTime = 300.0f;
                particleSysMain.maxParticles = 10000;
                particleSysCollision.enabled = true;
                particleSysCollision.type = ParticleSystemCollisionType.World;
            }
            else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.LOW)
            {
                    particleSysEmission.rateOverTime = 200.0f;
                    particleSysMain.maxParticles = 5000;
                    particleSysCollision.enabled = true;
                    particleSysCollision.type = ParticleSystemCollisionType.Planes;
                    for (int j = 0; j < CollisionPlanes.Length; j++)
                    {
                        particleSysCollision.SetPlane(j, CollisionPlanes[j]);
                    }
                }
            else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.OFF)
            {
                particleSysEmission.rateOverTime = 100.0f;
                particleSysMain.maxParticles = 3000;
                particleSysCollision.enabled = false;
            }
        }

CPUCapabilityTester示例中查看更多优化版本

实时全局照明 (GI)

概述

为了实现这个效果,请查看照明窗口 (Window > Lighting) 中的 实时照明 复选框 。 (注:实时 GI 的编辑器性能设置在 Unity* 的最新版本中隐藏,并在后台处理。脚本化 更新设置 仍然可用 – 参阅脚本以获取详细信息) 在 Unity* 早期版本中查看 预计算实时 GI 复选框 (同样位于  Window > Lighting中)。 实时 分辨率 和  CPU 使用是严重影响 CPU 利用率的两个设置。

  • 实时 分辨率 确定每个单元应计算多少个纹理像素。 Unity* 发布的一篇教程 详细介绍了如何正确设置该值。根据实用的常规经验,视觉效果丰富的室内场景需要更多的每单元纹理像素,才能实现最逼真的效果。在广阔的室外场景中,间接的 光线 过渡并不明显,支持将 计算 能力用于其他地方。
  • CPU 利用率 确定了引擎中可用于实时 GI 计算的工作线程数量。最好能确定各种系统级别的可用 CPU 性能,  并以此作为设置的依据。对于低端系统,最好设置为低/中;高端 系统 建议设置为高或不限。关于这些设置的描述,详见与相应版本一同发送的 Unity* 文档。


Unity* 5.6.1f1 中的设置


旧版 Unity* 中的设置

void Start () {
        SetCPULevel(CPUCapabilityManager.Singleton.CPUCapabilityLevel);
    }

public void SetCPULevel(CPUCapabilityManager.SYSTEM_LEVELS sysLevel)
    {
        if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.HIGH)
        {
            DynamicGI.updateThreshold = 0;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.MEDIUM)
        {
            DynamicGI.updateThreshold = 25;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.LOW)
        {
            DynamicGI.updateThreshold = 50;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.OFF)
        {
            DynamicGI.updateThreshold = 100;
        }
        Debug.Log("(" + gameObject.name + ") System capability set to:" + CPUCapabilityManager.Singleton.CPUCapabilityLevel + ", so setting GI update threshold to:" + DynamicGI.updateThreshold);
    }

概述

实时 GI模拟场景中光线的跳动与间接照亮对象。团队非常希望使用这个特性,因为 Aegis 前面的大窗户支持星体和破坏效果更新舰桥内部。在大型恒星或星云前移动 Aegis 改变了舰桥的外观,反射了入射光线,通过保持场景外观的一致和增强真实感,提供了更出色的沉浸式体验。

什么原因使它成为 CPU 密集型任务?

Unity 的* 实时 GI消耗了大量的 CPU 计算资源,根据要求的逼真度使用部分可用内核。

是否内置于 Unity*?

是的。当启用实时GI效果时,应用程序用最高CPU使用率设置Unity *,允许立即更新速率以获得最佳效果。

动态小行星

云尾与太阳闪焰

如何实现

如果玩家的设备足够先进,可以启用 刚体 物理,将场景中的静态模型变为动态模型。通过 script 在已有对象中添加全新 刚体 组件或动态生成预配置对象的预制件,便可在脚本中实现该目标。动态对象和可交互对象在增强游戏(尤其是虚拟现实游戏)的沉浸感中发挥了巨大作用。

public GameObject[] PotentiallyDynamicObjects;
    int NumDynamicObjects = 0;
    void Start () {
        SetCPULevel(CPUCapabilityManager.Singleton.CPUCapabilityLevel);
    }
    public void SetCPULevel(CPUCapabilityManager.SYSTEM_LEVELS sysLevel)
    {
        if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.HIGH)
        {
            NumDynamicObjects = PotentiallyDynamicObjects.Length;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.MEDIUM)
        {
            NumDynamicObjects = PotentiallyDynamicObjects.Length / 2;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.LOW)
        {
            NumDynamicObjects = PotentiallyDynamicObjects.Length / 3;
        }
        else if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.OFF)
        {
            NumDynamicObjects = 0;
        }

        Debug.Log("(Obj:"+ gameObject.name + ") System capability set to:" + CPUCapabilityManager.Singleton.CPUCapabilityLevel + ", so setting number of dynamic objects to:" + NumDynamicObjects);

        for (int i = 0; i < NumDynamicObjects; i++)
        {

            Rigidbody objRigidBody = PotentiallyDynamicObjects[i].AddComponent<Rigidbody>();
            objRigidBody.useGravity = true;
            objRigidBody.mass = 10;
            PotentiallyDynamicObjects[i].AddComponent<CustomAsteroidMovement>();
        }
    }

是否内置于 Unity*?

动态小行星使用了 Unity* 的 刚体物理 和 粒子系统,但是生成小行星的系统由《Star Trek™:Bridge Crew*》团队编写与定制。查看以下示例,了解如何自行实施类似系统。

概述

当 Aegis 航行至小行星带时,将在玩家的视图体之外生成额外的小行星,并将它们发射到视图。这些小行星与已就位的小行星发生碰撞,并搅起尘埃。

许多游戏地图还包括小行星带生成器,将大量静态小行星分散在圆柱或球形区域。启用高端 CPU 效果后,舰桥移动时,这些区域将在舰桥特定距离以外放置应用刚体物理的动态小行星。用户会产生这样的印象:小行星带内遍布小碎片,它们相互碰撞,并与更大的行星相撞。由于速度已被用于维持物体运动与场景的活跃,产生一个动态行星带的可能性很小。最后,某些小行星与玩家的战舰或其他小行星碰撞后,将分解为更小的碎片,其它小行星将完好无损地反弹。

这些改变将用户的注意力从天空盒移开,使用户感觉自己身处太空;同时不中断游戏体验。

什么原因使它成为 CPU 密集型任务?

借助刚体物理使小行星带内漂浮着大量动态小行星碎片,实例化未合并的碎片以及在小行星解体后移动和生成额外碎片都使用了大量 CPU 时间。

概述

云尾制造了敌方战舰和 Aegis 通过太空时搅动尘埃的假象,增强了沉浸感。太阳闪焰通过将玩家目光从天空盒移开,使玩家认为自己身处遥远的太空,实现了相同的效果。

什么原因使它成为 CPU 密集型任务?

云尾与太阳闪焰使用了脚本化的粒子行为,该行为要求使用主线程上的脚本单独更新粒子。从循环数百个粒子到循环数千个粒子以及通过脚本更新属性占用了大量 CPU 时间,但是支持粒子的自定义行为,这是 Unity* 提供的即购即用普通粒子系统属性无法实现的。请记住,目前必须在主线程上完成,以防止系统像前面提到的粒子碰撞系统那样使用过多内核。请继续关注 Unity* 在 Unite Europe 2017大会上提到的全新 C# 工作系统,后者将通过扩展 Unity* API 优化脚本代码中的多线程。

是否内置于 Unity*?

云尾和太阳闪焰使用了 Unity* 的粒子系统,但是 Red Storm Entertainment 通过编辑脚本,确定粒子如何随时间的推移而移动和改变。尾流效应使用一个粒子系统,从战舰的若干发射器点发射粒子拖尾。单个拖尾内粒子的大小和生命周期取决于它的发射器。拖尾粒子在世界空间内发射,但是发射器点一直附着于战舰,以确保在战舰转向与倾斜时也能从正确位置发射。定制粒子行为脚本在战舰后面添加虚拟“吸引器”对象,该对象通过随机振荡拉回附近的粒子,穿过云层时向战舰后面的拖尾引入湍流。太阳闪焰也使用吸引器行为向外散开粒子,或在向外发射粒子后,将其拉回太阳表面。以下简单示例展示了如何使所有粒子飞向世界起源点。

public ParticleSystem MyParticleSystem;
    ParticleSystem.Particle[] MyParticles = new ParticleSystem.Particle[4000];
    public float ParticleSpeed = 10.0f;

	void Update () {
        int numParticles = MyParticleSystem.GetParticles(MyParticles);

        for(int i = 0; i < numParticles; i++)
        {
            MyParticles[i].position = Vector3.MoveTowards(MyParticles[i].position, Vector3.zero, Time.deltaTime * ParticleSpeed);
        }

        MyParticleSystem.SetParticles(MyParticles, numParticles);
	}

摧毁战舰

如何实现

将模型建造为碎片,以各种断点作为间隔。为 Unity* 中包含网格渲染器的每个游戏对象配备一个 刚体 组件。对象被摧毁后,在每个网格上启用 刚体 组件,并将爆炸力应用到所有网格。如欲了解更多详情,请参阅 Unity* 的 刚体文档 。

// Explosion arguments
    public float ExplosiveForce;
    public float ExplosiveRadius;
    public Transform ExplosiveTransform;    // Centerpoint of explosion

    public Rigidbody BaseRigidBody;
    public GameObject[] PotentiallyDetachableCubes;
    List<Rigidbody> ObjRigidbodies = new List<Rigidbody>();
    bool IsCPUCapable = false;
    bool HasExploded = false;
	void Start ()
    {
        SetCPULevel(CPUCapabilityManager.Singleton.CPUCapabilityLevel);
    }

    public void SetCPULevel(CPUCapabilityManager.SYSTEM_LEVELS sysLevel)
    {
        // Only use if CPU deemed medium or high capability
        if (sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.HIGH
            || sysLevel == CPUCapabilityManager.SYSTEM_LEVELS.MEDIUM)
        {
            IsCPUCapable = true;

            // add rigidbodies to all little cubes
            for (int i = 0; i < PotentiallyDetachableCubes.Length; i++)
            {
                Rigidbody CurrRigidbody = PotentiallyDetachableCubes[i].AddComponent<Rigidbody>();
                CurrRigidbody.isKinematic = true;
                CurrRigidbody.useGravity = false;
                ObjRigidbodies.Add(CurrRigidbody);
            }
            Debug.Log("(ExplosionController) System capability set to:" + CPUCapabilityManager.Singleton.CPUCapabilityLevel + ", so object (" + gameObject.name + ") is destructible");
        }
        else
        {

            Debug.Log("(ExplosionController) System capability set to:" + CPUCapabilityManager.Singleton.CPUCapabilityLevel + ", so object (" + gameObject.name + ") not destructible");
        }
    }

    public void ExplodeObject()
    {
        HasExploded = true;
        if (IsCPUCapable)
        {
            BaseRigidBody.useGravity = false;
            BaseRigidBody.isKinematic = true;
            BoxCollider[] BaseColliders = GetComponents<BoxCollider>();
            for(int i = 0; i < BaseColliders.Length; i++)
            {
                BaseColliders[i].enabled = false;
            }
            for (int i = 0; i < ObjRigidbodies.Count; i++)
            {
                Rigidbody CurrRigidbody = ObjRigidbodies[i];
                CurrRigidbody.isKinematic = false;
                CurrRigidbody.useGravity = true;
                CurrRigidbody.AddExplosionForce(ExplosiveForce, ExplosiveTransform.position, ExplosiveRadius);
                ObjRigidbodies[i].gameObject.AddComponent<BoxCollider>();
            }
        }
        else
        {
            // Boring destruction implementation
            BaseRigidBody.AddExplosionForce(ExplosiveForce, ExplosiveTransform.position, ExplosiveRadius);
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if(!HasExploded)
        {
            ExplosiveTransform.position = collision.contacts[0].point;
            ExplodeObject();
        }
    }

概述

战舰摧毁特性使玩家击败敌人时满足感倍增,从而增加了游戏的刺激性。在游戏中,一般会通过爆炸效果遮挡爆炸对象,以掩饰从场景中删除废弃 游戏对象时使用的 popping 效果。借助高端配置中的 CPU 性能,我们可以将模型分成若干部分,朝不同方向发射,甚至可以添加子摧毁。每个碎片都可以与场景装置相撞,最终消失或停留(如果系统能够处理)。

什么原因使它成为 CPU 密集型任务?

艺术家将战舰分解为包含刚体组件的多个不同部分,初始化各个部分后通过物理力制作动画。启动与其他对象(如小行星或战舰)的碰撞,以确保在动画环境中显示逼真的行为。此外,战舰的每块爆炸碎片都添加了粒子拖尾。

是否内置于 Unity*?

该特性的刚体和物理属性完全内置,使用针对 Unity* 的方法添加爆炸力至战舰碎片。随后制作动画,并借助 Unity* 的刚体物理系统与对象碰撞。Unity* 粒子系统用于发射带有子发射器的粒子,以在碎片后面创建拖尾,但是最高级别的粒子位置由脚本管理,以确保它们依附于爆炸的战舰碎片,无需担心父项的坐标空间。

CPU 性能检测插件

我们已经介绍了《Star Trek™:Bridge Crew*》中添加的所有特性,但是,如何确定目标系统能处理哪些特性?为了尽量减少麻烦,我们创建了一个易于使用的 Unity*插件(包含源代码)。代码附有面向 Unity* 和原生实施的示例代码,用作一个工具盒,为您提供帮助定义目标系统类型的系统指标。上文的许多示例已经集成至样本,方便用户使用。包括以下步骤:

  1. 定义您的 CPU 性能级别。
        public enum SYSTEM_LEVELS
        {
            OFF,
            LOW,
            MEDIUM,
            HIGH,
            NUM_SYSTEMS
        };
  2. 设置 CPU 值阈值。插件提供了各种指标,如逻辑/物理内核数量、最大频率、系统内存等。但是,如果您想考虑其他因素,可以随时添加其他指标。对于多数基本使用,提供的指标应该够用。
            // i5-4590
            LowSettings.NumLogicalCores = 4;
            LowSettings.UsablePhysMemoryGB = 8;
            LowSettings.MaxBaseFrequency = 3.3;
            LowSettings.CacheSizeMB = 6;
    
            // i7 - 7820HK - Set to turbo mode
            MedSettings.NumLogicalCores = 8;
            MedSettings.UsablePhysMemoryGB = 8;
            MedSettings.MaxBaseFrequency = 3.9;
            MedSettings.CacheSizeMB = 8;
    
            // i7-6700k
            HighSettings.NumLogicalCores = 8;
            HighSettings.UsablePhysMemoryGB = 8;
            HighSettings.MaxBaseFrequency = 4.0;
            HighSettings.CacheSizeMB = 8;
    
  3. 初始化插件并确定用户是否运行英特尔® 处理器。
    void QueryCPU()
        {
            InitializeResources();
            if (IsIntelCPU())
            {
                // Your performance categorization code
            }
            else
            {
                Debug.Log("You are not running on an Intel CPU");
            }
        }
  4. 询问目标系统。
    StringBuilder cpuNameBuffer = new StringBuilder(BufferSize);
                GetProcessorName(cpuNameBuffer, ref BufferSize);
                SysLogicalCores = GetNumLogicalCores();
                SysUsablePhysMemoryGB = GetUsablePhysMemoryGB();
                SysMaxBaseFrequency = GetMaxBaseFrequency();
                SysCacheSizeMB = GetCacheSizeMB();
  5. 对比阙值,以确定测试系统属于前面定义的何种性能级别。
        bool IsSystemHigherThanThreshold(SystemThreshold threshold)
        {
            if (threshold.NumLogicalCores < SysLogicalCores && threshold.MaxBaseFrequency < SysMaxBaseFrequency&& threshold.UsablePhysMemoryGB < SysUsablePhysMemoryGB && threshold.CacheSizeMB < SysCacheSizeMB)
            {
                return true;
            }
            return false;
        }
    SYSTEM_LEVELS MySystemLevel = SYSTEM_LEVELS.OFF;
    
    if (IsSystemHigherThanThreshold(HighSettings) || IsWhitelistedCPU(SYSTEM_LEVELS.HIGH))
            {
                MySystemLevel = SYSTEM_LEVELS.HIGH;
            }
            else if (IsSystemHigherThanThreshold(MedSettings) || IsWhitelistedCPU(SYSTEM_LEVELS.MEDIUM))
            {
                MySystemLevel = SYSTEM_LEVELS.MEDIUM;
            }
            else if (IsSystemHigherThanThreshold(LowSettings) || IsWhitelistedCPU(SYSTEM_LEVELS.OFF))
            {
                MySystemLevel = SYSTEM_LEVELS.LOW;
            }
            else
            {
                MySystemLevel = SYSTEM_LEVELS.OFF;
            }
    
            Debug.Log("Your system level has been categorized as:" + MySystemLevel);

性能分析与注意事项

和 GPU 工作相同,我们需要验证特性集的组合 CPU 利用率没有超过目标,以持续提供异步空间扭曲(极力避免 Star Trek™ 的双关语)和二次投影触发器。我们想要确保无论运行于何种设备,游戏的每秒帧数能维持在 90,同时最大限度地使用 CPU。《Star Trek™:Bridge Crew*》团队决定将特性集分为 3 个等级:OffPartialFull。因此,我们在匹配 Off阙值的设备上测试了Full组特性。


GPUView 显示配备 HSW i5-4590 CPU + GTX 1080 GPU 的台式机系统的工作分布

CPU显卡场景配置运行更新周期新帧掉帧生成的合成帧

HSW i5-4590

GTX1080

任务 5,初始弯曲之后

完整设置

1

11861

5993

58

5810

2

11731

6584

56

5091

3

11909

6175

101

5633

 

 

平均值

11833.67

6250.67

71.67

5511.33

生成的合成帧数不为零,表示在低端 CPU 上完整特性集的 CPU 工作超过每帧 11.1 毫秒的阙值

以上 GPUView 截屏显示从 presentpresent耗时约 22 毫秒(突出显示)。Present表示最后帧已生成并且为提交至 头盔显示器 (HMD)做好准备的时间,可以从帧速率(转换为 45 fps)的角度理解。从 90 到 45 fps 意味着我们正不断通过运行于“Off”级系统的配置触发 ASW。查看任务 5 的 3 次测试运行,我们发现 ASW触发器平均生成了约 5500 个合成帧。将这些沉浸式特性集成至 Oculus min-spec 便失去了效果,这和我们预想的一致。我们没有在所有配置中删除特性集,而是将特性集绑定运行时可确定的硬件级别,以激活合适的特性集,为不同硬件级别的玩家提供最佳游戏体验。如果查看运行于高端目标(英特尔® 酷睿 i7-7700K 处理器)的相同配置,会发现不同的结果。


GPUView 显示搭载 KBL i7-7700K CPU + GTX 1080 GPU 的台式机系统上的工作分布

CPU显卡场景配置运行更新周期新帧掉帧生成的合成帧

KBL i7-7700k

GTX1080

任务 5,初始弯曲之后

完整设置

1

11703

11666

37

0

2

11654

11617

37

0

3

11700

11672

28

0

 

 

平均值

11685.67

11651.67

34.00

0.00

生成的合成帧数为零,表示高端 CPU 上完整特性集的 CPU 工作未超过每帧 11.1 毫秒的阙值

借助高端目标提供的额外的逻辑内核、更高的频率和更大的高速缓存,所有工作都将扩展,并能在 11.1 毫秒的规定时间内完成,以达到 90 fps 的帧速率。整个过程中,CPU 工作的平均每帧持续时间在 9-10.3 毫秒之间。这意味着高端目标的性能几乎达到了极限,但是仍维持稳定的 90 fps,并使用了所有的可用资源。我们达到了最佳结果!我们完成了“Off”和“Full”特性集的测试。此时,我们需要选择“Full”特性集中的一个子集,并在基于英特尔酷睿 i7-7700HK 处理器的 VR 就绪型笔记本电脑上启用该子集。这是“Partial”特性集的中间目标。我们希望保留真正影响舰桥内部的特性,因此我们提高这些特性的优先级,然后逐一移除其他特性,直到得到最佳结果。最终,我们只需移除动态尾流效应和动态小行星,便可在笔记本电脑上轻松实现 90 fps 的帧速率。以下 GPUView* 截屏显示了运行于 VR 就绪型测试笔记本电脑的“Partial”特性集。


GPUView 显示搭载 KBL i7-7820HK CPU + GTX 1080 GPU 的虚拟现实游戏笔记本电脑上的工作分布

CPU显卡场景配置运行更新周期新帧掉帧生成的合成帧

KBL i7-7820HK

GTX1080

任务 5,初始弯曲之后

完整设置

1

11887

11242

116

529

2

11881

11315

110

456

3

11792

10912

125

755

 

 

平均值

11853.33

11156.33

117.00

580.00

生成的合成帧数不为零,表示在 VR 就绪型笔记本电脑上完整特性集的 CPU 工作超过每帧 11.1 毫秒的阙值

CPU显卡场景配置运行更新周期新帧掉帧生成的合成帧

KBL i7-7820HK

GTX1080

任务 5,初始弯曲之后

部分设置

1

11882

11844

38

0

2

10171

10146

25

0

3

11971

11933

38

0

 

 

平均值

11341.33

11307.67

33.67

0.00

生成的合成帧数为零,表示在 VR 就绪型笔记本电脑上部分特性集的 CPU 工作未超过每帧 11.1 毫秒的阙值

结论

总体而言,更逼真、更高分辨率的模拟以及更多动态实体的使用是 CPU 利用率上升的主要原因;现在可以在许多 CPU 上启用之前较昂贵的物理模拟。此外,可以使用动画/反向运动学 (IK)、布料模拟、群集、流体模拟、程序生成等其他 CPU 密集型系统生成更丰富、更逼真的世界。行业内已存在面向显卡的设置级别,现在我们应着手考虑为 CPU 设置划分级别。开发游戏时,请思考各个硬件级别上未被充分挖掘的所有计算潜能,考虑如何利用这些潜能使游戏变得与众不同。如欲获取更多信息,请点击以下链接。尽情探索。

  • 特别鸣谢 Kevin Coyle 和 Red Storm Entertainment 团队的其他成员,他们与我们通力合作,协助文章的完成**

其他资源

“发挥震撼的显卡性能:使用 CPU 增强《Star Trek™:Bridge Crew》的虚拟现实沉浸感”

作者在 Unite 2016 大会上展示了文章的信息。

会议描述– 如今,许多游戏和体特别强调 GPU 工作,但是内置于现代主流 CPU 的多核处于闲置状态。此演讲探索了 Ubisoft 的 Red Storm 工作室和英特尔如何通过使用 Unity* 在《Star Trek™:Bridge Crew》中提供最卓越的沉浸感,以充分利用可用资源。了解如何在您的游戏中实现震撼的视觉效果,同时将对 GPU 的性能影响降至最低!

Catlike 编码

Catlike 编码提供众多出色的 CPU/数学密集型教程,任何人可以轻松掌握与运行。本教程以 Unity* 为主,但是由于内容不依赖任何特定的 API,因此适用于所有引擎。强烈推荐对程序生成、曲线/样条使用、网格变形、纹理处理、噪声等感兴趣的用户使用该教程。

面向视频游戏的流体模拟(系列)

本文是一篇编写出色的教程,介绍了如何实施面向视频游戏的多核流体模拟。本文非常适合初学者,涵盖了从概念到实施的所有信息。文章结尾处将提供源代码,读者可以将其添加至引擎,了解如何使用代码模拟各种流体类型。

链接:https://software.intel.com/zh-cn/articles/fluid-simulation-for-video-games-part-1

在Windows*上编译Tensorflow教程

$
0
0

作者:Gu, Jianjun

背景介绍

最简单的 Tensorflow 的安装方法是在 pip 一键式安装官方预编译好的包

pip install tensorflow

通常这种预编译的包的编译参数选择是为了最大兼容性而不是为了最优性能,导致在使用过程中,每次运行代码都会输出一大堆的 warning 信息。例如在安装了谷歌官方的 Tensorflow 1.3.0 包后,运行以下测试代码时

import tensorflow as tf

hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

Console 会输出

C:\Users\jgu\Anaconda3\python.exe C:/Users/sandman/PycharmProjects/untitled/tf_helloworld.py
2017-10-27 13:42:20.005261: W C:\work\tensorflow-1.3.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
2017-10-27 13:42:20.005475: W C:\work\tensorflow-1.3.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.
b'Hello, TensorFlow!'

Process finished with exit code 0

表示此 Tensorflow 版本只用到了 CPU SSE 指令集优化,可以运行在很多老架构的 CPU 指令集上。
为了充分利用 AVX/AVX2 来加速 Tensorflow 的 CPU 版本的计算速度,需要自己下载 Tensorflow 的源码,在编译时使用这些指令集。
以下教程基于最新的Tensorflow 1.3.1源码,利用用Visual Studio 2015/Visual Studio 2017来编译一个基于 AVX/AVX2 的 CPU 版本的 Tensorflow Python 安装包。

编译过程

准备工作

VS2015编译过程

  • 打开 VS2015 64 位命令行编译环境
  • 在命令行环境中进入 Tensorflow 的源码路径 tensorflow-1.3.1\tensorflow\contrib\cmake
    新建一个文件夹 build2015,进入 build2015 文件夹
  • 用 CMake 生成 VS2015 的编译项目
    C:\...\build2015>cmake .. -A x64 -DCMAKE_BUILD_TYPE=Release
    -DSWIG_EXECUTABLE=C:\work\swigwin-3.0.12\swig.exe
    -DPYTHON_EXECUTABLE="C:\Users\jgu23\AppData\Local\Continuum\Anaconda3\python.exe"
    -DPYTHON_LIBRARIES="C:\Users\jgu23\AppData\Local\Continuum\Anaconda3\libs\python36.lib"
    -Dtensorflow_WIN_CPU_SIMD_OPTIONS=/arch:AVX	
  • 用 MSBuild 编译生成 Tensorflow 的 pip 安装包
    C:\...\build2015>MSBuild /p:Configuration=Release tf_python_build_pip_package.vcxproj
    编译时需要联网,git 会从网上下载必要的项目包
    经过漫长的编译过程(我的编译环境是 i5 7440hq + 12G 内存)
  • pip 安装 Tensorflow pip 安装包
    如果编译没错误的话,进入到 C:\...\build2015\tf_python\dist 目录下,可以看到一个文件
    tensorflow-1.3.1-cp36-cp36m-win_amd64.whl
    这个就是编译出来的 Tensorflow 安装包
    在进入 Anaconda Prompt 界面

    进入到 C:\...\build2017\tf_python\dist 目录下,输入“pip install tensorflow-1.3.1-cp36-cp36m-win_amd64.whl”
    到这里,Tensorflow 就已经编译安装成功了
  • 最后验证一下
    运行代码
    import tensorflow as tf
    
    hello = tf.constant('Hello, TensorFlow!')
    sess = tf.Session()
    print(sess.run(hello))		
    Console 输出
    C:\Users\jgu\Anaconda3\python.exe C:/Users/sandman/PycharmProjects/untitled/tf_helloworld.py
    2017-10-27 13:48:20.005261: W C:\work\tensorflow-1.3.1\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX2 instructions, but these are available on your machine and could speed up CPU computations.
    b'Hello, TensorFlow!'
    说明这个 Tensorflow 包支持 AVX 指令集
  • 注意
    VS2015 编译 Tensorflow 最高只支持到 AVX, 如果 CMake 生成编译项目时指定 CPU 指令集支持到 AVX2,编译时将出现 error C3861: 'xxx': identifier not found 错误。要支持 AVX2 指令集,必须用 VS2017 编译

    查看大图

VS2017编译过程

  • 打开 VS2017 64 位命令行编译环境
  • 在命令行环境中进入 Tensorflow 的源码路径 tensorflow-1.3.1\tensorflow\contrib\cmake
    新建一个文件夹 build2017,进入 build2017 文件夹
  • 用 CMake 生成 VS2017 的编译项目
    C:\...\build2017>cmake .. -A x64 -DCMAKE_BUILD_TYPE=Release
    -DSWIG_EXECUTABLE=C:\work\swigwin-3.0.12\swig.exe
    -DPYTHON_EXECUTABLE="C:\Users\jgu23\AppData\Local\Continuum\Anaconda3\python.exe"
    -DPYTHON_LIBRARIES="C:\Users\jgu23\AppData\Local\Continuum\Anaconda3\libs\python36.lib"
    -Dtensorflow_WIN_CPU_SIMD_OPTIONS=/arch:AVX2
  • 这里比 VS2015 的编译多了一步
    用 VS2017 打开 tf_core_kernels.vcxproj


    进入 Properties->Configuration Properties->VC++ Directories->Executable Directories



    将 Executable Directories下的 $(VC_ExecutablePath_x64) 改为 $(VC_ExecutablePath_x64_x64)


    保存项目,退出
  • 用 MSBuild 编译生成 Tensorflow 的 pip 安装包
    C:\...\build2017>MSBuild /p:Configuration=Release tf_python_build_pip_package.vcxproj
    编译时需要联网,git 会从网上下载必要的项目包
    经过漫长的编译过程(我的编译环境是 i5 7440hq + 12G 内存)
  • pip安装 Tensorflow 安装包
    如果编译没错误的话,进入到 C:\...\build2017\tf_python\dist目录下,可以看到一个文件
    tensorflow-1.3.1-cp36-cp36m-win_amd64.whl
    这个就是 Tensorflow 的安装包
    在进入 Anaconda Prompt 界面

    进入到 C:\...\build2017\tf_python\dist 目录下,输入 “pip install tensorflow-1.3.1-cp36-cp36m-win_amd64.whl”
    到这里,Tensorflow 就已经编译安装成功了
  • 最后验证一下
    运行代码
    import tensorflow as tf
    
    hello = tf.constant('Hello, TensorFlow!')
    sess = tf.Session()
    print(sess.run(hello))
    Console输出
    C:\Users\jgu\Anaconda3\python.exe C:/Users/sandman/PycharmProjects/untitled/tf_helloworld.py
    b'Hello, TensorFlow!'
    
    Process finished with exit code 0
    
    没有任何 warning 输出,说明这个 Tensorflow 包支持 AVX2 指令集

常见错误及解决方法

编译 re2.vcxproj 时出错

warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss
以及
Error C2001: Newline in constant

错误原因:此错误和编译平台的 windows 操作系统的locale设为中文有关,英文的 windows 没有这个错误
解决办法: 修改 CMakeCache.txt,让 MSBuild 编译这个项目时,强制按照 utf-8 编码文件的格式来解析文件
进入 C:\...\tensorflow-1.3.1\tensorflow\contrib\cmake\build2017\re2\src\re2 目录
用文本编辑器打开 CMakeCache.txt,找到以下2行



在 CMAKE_CXX_FLAGS_RELEASE 这里添加蓝色部分,修改为
CMAKE_CXX_FLAGS_RELEASE:STRING=/MD /O2 /Ob2 /DNDEBUG /source-charset:utf-8

编译 tf_core_kernels.vcxproj 时出错

fatal error c1060: the compiler is out of heap space
以及
fatal error C1002: compiler is out of heap space in pass 2

错误原因:VS 编译环境默认的编译工具链为32位,Tensorflow 编译时会消耗大量的内存,导致编译器消耗的内存超出了32位编译器的寻址范围。
解决方法:

  1. VS2015
    需要运行 64 位命令行编译环境,在“开始”菜单中选择运行 “VS2015 x64 Native Tools Command Prompt”,如本文 2.2 章中“打开 VS2015 64 位命令行编译环境”部分所示
    或者在命令行里手工切换
    首先进入 VS2015 的安装目录 “cd C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC”
    输入“vcvarsall amd64”
    参考 MSDN “How to: Enable a 64-Bit Visual C++ Toolset on the Command Line”
    https://msdn.microsoft.com/en-us/library/x4d2c09s.aspx
  2. VS2017
    可能因为 Tensorflow 项目中 CMake 脚本对 VS2017 支持不好,所以无法按照 VS2015 的修改办法来通过指定64位编译环境的方法来解决这个问题。我们需要用 VS2017 打开 tf_core_kernels.vcxproj,手工将 Properties->Configuration Properties->VC++ Directories->Executable Directories 下的$(VC_ExecutablePath_x64)改为$(VC_ExecutablePath_x64_x64)
    如本文 2.3 章中“VS2017 打开 tf_core_kernels.vcxproj”部分所示

结论

通过手工编译 Tensorflow 源码来支持 AVX/AVX2 指令,可以消除运行 Tensorflow 程序时烦人的 warning 提示,并且可以获得比官方预编译版本更快的 Tensorflow 学习/推理速度,节省了 Tensorflow 开发者的时间。

Caffe学习笔记 第一部分 - Windows*下BVLC Caffe的安装与配置

$
0
0

作者:Gu, Jianjun

Tensorflow和Caffe是机器学习初学者常用的2种深度学习框架。相对于Tensorflow有简单的python pip一键安装包,Caffe的安装更考验开发者的计算机编译水平,需要自己下载源码编译。尽管caffe的配置教程网上很多,但是网上caffe的各个分支版本也很多,所以初学者在编译时总是会碰到各种奇怪的问题。本文会针对初学者常用的Windows* + Intel CPU的平台上安装配置Caffe做一个较为完整的总结。

Caffe的各个版本简介

官方版本:伯克利BVLC(Berkeley Vision And Learning Center) 版

https://github.com/BVLC/caffe
这个是Caffe的主版本,由伯克利大学维护。其他所有的Caffe版本都是由这个版本分支出去的。这个版本Caffe可以编译出Linux和Windows*版本,支持Nvidia的CUDA加速,但是对CPU的优化不好,而且有关安装和配置的文档很少。从这个版本编译Caffe,需要很强的自我学习和解决问题能力。

微软的Windows*版本

https://github.com/Microsoft/caffe
微软自己维护的一个版本。这个版本简化了Caffe在Windows*下的步骤,自带了一个VS的Solution项目。这个版本同样支持基于CPU和CUDA的算法实现。

Intel Caffe优化版

https://github.com/intel/caffe
Intel维护的版本,这个版本优化了Intel CPU的实现算法,同时提供对Intel Xeon CPU多核和多节点的支持。这个版本里有些算法库对Windows*支持不太好,所以这个分支编译出的Caffe主要还是运行在Linux的服务器平台上做训练工作。

Intel clCaffe 核显OpenCL优化版

https://github.com/01org/caffe
Intel维护的另一个版本,相对于官网BVLC Caffe上的OpenCL分支,优化了基于Intel 核显GPU的OpenCL加速算法。这个版本同时支持Windows* 和Linux平台,适合在有Intel核显的CPU上做一些推理(Inference)场景的工作。

BVLC caffe的编译

安装编译所需要的软件

配置Windows*的环境变量

为了避免等会CMAKE生成编译脚本的时候找不到一些依赖关系,有的没的路径都加一些,包括Cmake, Git, Ananconda以及Python的路径。

Checkout 官网Caffe的代码

运行以下代码,用git从官网caffe上下载Windows*分支。

git clone https://github.com/BVLC/caffe.git
cd caffe
git checkout windows

修改caffe源码代码里的一些编译参数

  • 修改scripts\build_win.cmd
    因为我们没有定义APPVEYOR,所以直接拉到else(大约69行)以后。
    先从APPVEYOR部分把这部分内容拷贝过来
if !PYTHON_VERSION! EQU 2 (
    set CONDA_ROOT=C:\Miniconda-x64
)
:: Set python 3.5 with conda as the default python
if !PYTHON_VERSION! EQU 3 (
    set CONDA_ROOT=C:\Miniconda35-x64
)
set PATH=!CONDA_ROOT!;!CONDA_ROOT!\Scripts;!CONDA_ROOT!\Library\bin;!PATH!

然后根据Windows*环境设置下图中红色方框的部分

1. 编译器是VS2015,设置MSVC_VERSION=14

2. 不需要NINJA来编译,设置WITH_NINJA=0

3. 没有NVDIA GPU,设置CPU_ONLY=1

4. Python版本为3.X,设置PYTHON_VERSION=3

5. 需要pyCaffe支持,设置BUILD_PYTHON=1

  • 如果Python版本不是2.7或3.5,修改
    cmake\WindowsDownloadPrebuiltDependencies.cmake
    Caffe在编译时会自动从网上下载一个依赖库caffe-builder,这个cmake文件负责根据你系统里的VS版本和python版本自动下载预编译好的caffe-builder包。目前网上只有预编译好的VS2013/2015配python2.7/3.5的caffe-builder,如果是python3.6,编译时会报找不到VS2015配python3.6的caffe-builder包。解决方法有2个办法,要么用python3.5的包,要么自己编译caffe-builder。

    对于复用Python3.5的包,可以按照下图中所示,复制DEPENDENCIES_URL_1900_35和DEPENDENCIES_SHA_1900_35 2行,同时修改35为36。

如果希望使用自己编译VS2015加python3.6的caffe-builder,除了要加入上段说到的2行修改外,还需要注释掉下图中WindowsDownloadPrebuiltDependencies.cmake的下图所示的绿色部分,这部分代码是负责从网上下载依赖包并且解压缩到本地目录的。同时要把caffe-builder编译出的libraries目录拷贝到C:\Users\[user name]\.caffe\dependencies这个目录下。(具体caffe-builder的编译,将在本文第3节详细介绍)

开始编译

在caffe的目录下输入scripts\build_win.cmd

开始编译,一切顺利的话,大约10分钟后就编译好了

验证一下编译的结果

接下来运行一下caffe项目自带的examples里的00-classification的代码来验证一下caffe是否能够正常运行

打开anaconda的命令行,进入caffe的examples目录,运行jupyter notebook

在打开的notebook中打开caffe自带的例子 00-calssification.ipynb

这是用一个训练好的Caffe模型来预测动物图片的例子,图片默认是使用Caffe项目里examples\image\cat.jpg。

一路Shift+Enter运行下去,看到第8步predicted输出

predicted class is: 281

第9步输出

output label: n02123045 tabby, tabby cat

预测结果是猫,说明caffe已经正确编译而且能运行了。 

大功告成,开始你的Caffe学习之旅吧

Caffe依赖库Caffe Builder的编译

这一章主要介绍怎么编译Caffe-Builder项目

安装编译所需要的软件

编译软件的需求同2.1部分

配置Windows*的环境变量

环境变量的配置同2.2部分

下载Caffe-Builder源码

Caffe-Builder的开源项目位于 https://github.com/willyd/caffe-builder
目前最新的release为1.1版,可以从这里下载最新的release 1.1.0版的源码
https://github.com/willyd/caffe-builder/releases

修改caffe-builder源码代码里的一些编译参数

修改主要基于2个方面:
首先在Windows*下Ninja编译系统不容易配置好,所以这里选择了Visual Studio 2015作为编译器。修改build_v140_x64.cmd,将红色部分的’Ninja’改为’Visual Studio 14 2015 Win64’

其次是这个Release 1.1.0发布有点时间了,caffe-builder很多依赖的开源项目都搬家或者版本更新了,需要做相应的修改。

1) 修改packages\protobuf\CmakeLists.txt
将图中所示部分protobuf包的Hash值从原来的
14a532a7538551d5def317bfca41dace
修改为
39d6a4fa549c0cce164aa3064b1492dc

2) 修改packages\hdf5\CmakeLists.txt
将图中所示部分的URL从https://www.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8.16/src/CMkae-hdf5-1.8.16.zip修改为 https://www.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8/hdf5-1.8.16/src/CMkae-hdf5-1.8.16.zip

编译

进到caffe-builder-1.1.0目录下,运行build_v140_x64.cmd开始编译

屏幕输出

最终编译生产的caffe依赖库文件放在build\libraries目录下,你可以将这个libraries目录拷贝到caffe所需要的目录下。

小结

本篇文章主要介绍了官方BVLC Caffe在Windows*下的编译设置过程。BVLC Caffe提供CPU和Nvidia GPU版本的实现,但是在实际学习工作中,初级开发者的电脑平台通常不会装备昂贵的Nvidia显卡,而CPU版本的Caffe因为执行效率不高,只能用来做一些小型的”玩具”实验项目。

下一篇文章,会介绍一种基于Intel集成GPU核显加速的clCaffe框架。通过 clCaffe框架,开发者可以在中小型项目开发中在硬件成本和产品性能之间找到一个很好的平衡点。

Caffe学习笔记 第二部分 - Windows* 下基于Intel核显加速的clCaffe的安装,配置与性能提升

$
0
0

作者:Gu, Jianjun

点击访问Caffe学习笔记 第一部分 - Windows*下BVLC Caffe的安装与配置

clCaffe编译与配置

Intel clCaffe (https://github.com/01org/caffe)是利用基于Intel Skylake及以后的处理器核显(即Gen9架构以上)做硬件加速的一个修改版。如果你当前机器是基于Nvdia显卡,请用NV cuda加速版本;如果你的显卡是AMD的,请check out 官方BVLC caffe的opencl分支。

clCaffe的编译

安装编译所需要的软件

配置Windows*的环境变量

为了避免等会CMAKE生成编译脚本的时候找不到一些依赖关系,有的没的路径都加一些,包括Cmake, Git, Ananconda以及Python的路径。(环境变量的配置同上篇文章的2.2部分 )

下载和编译Intel clCaffe工程

clCaffe的编译过程和caffe基本类似。不同的是clCaffe所需的caffe-builder的libraries目录是放在clCaffe自己目录的build目录下,同时额外多下载编译了一些支持openCL运行的开源项目。为了简单起见,这里参考了一个开源项目里的编译脚本https://github.com/liyuming1978/caffe_example/blob/master/install_scripts/Windows_install/build-clcaffe.cmd

首先先创建一个clcaffe-windows的目录,下面提供了一个简单的编译脚本build-clcaffe.cmd(为了简化clCaffe的编译过程,这里直接提供了完整的编译脚本,不再解释脚本里每一步的具体目的,有兴趣的开发者可以自己研究修改脚本来满足自己的需求),在clcaffe-windows目录下创建并且执行这个批处理脚本文件。这个脚本是根据我自己的环境 Win10+VS2015+Python3.6写的,如果你的开发环境跟我的不同,比如是python2.7或者3.5,需要按照脚本里的注释做相应的修改。

@echo off
@setlocal EnableDelayedExpansion

echo must install openclsdk,python(anaconda),git,cmake,vs 2015 for desktop

::设置python所在路径, 基于python3,
::如果编译环境是python2, 需要修改下面print()这句话,按照python2的语法格式修改
for /f "delims=" %%t in ('python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"') do set py_path_str=%%t

::下载并编译clCaffe所需要的一些依赖项目
cd %~sdp0
git clone https://github.com/dlfcn-win32/dlfcn-win32
cd dlfcn-win32
cmake -G "Visual Studio 14 2015 Win64" .
cmake --build . --config Release
cd %~sdp0
::git clone https://github.com/ptillet/isaac.git (isaac build wrong, please use intel isaac)
git clone https://github.com/intel/isaac.git
cd isaac
mkdir build
cd build
cmake -G "Visual Studio 14 2015 Win64" ..
cmake --build . --config Release

::下载clCaffe的项目
cd %~sdp0
git clone https://github.com/01org/caffe.git
cd caffe
git checkout inference-optimize
git pull
git clone https://github.com/viennacl/viennacl-dev.git

::下面这部分是拷贝自己编译的caffe-builder的libraries到clCaffe的目录里,同时编译时需要
::修改WindowsDownloadPrebuiltDependencies.cmake,注释掉对应的网络下载和解压缩代码
::防止WindowsDownloadPrebuiltDependencies.cmake脚本报找不到网上对应的caffe-builder包,
:: 如果编译环境是python2.7或者3.5, 可以注释掉下面这段代码, 编译脚本会自动从网上下载预编译好的依赖库
cd %~sdp0
cd caffe
mkdir build
cd .\build
mkdir libraries
cd ..
xcopy C:\work\caffe-builder-1.1.0\build_v140_x64\libraries .\build\libraries /s /h /c /y

::设置编译参数,开始编译clCaffe
cd %~sdp0
cd caffe
set BUILD_PYTHON=1
set BUILD_PYTHON_LAYER=1
set USE_INTEL_SPATIAL=1
set USE_GREENTEA=1
set USE_ISAAC=1
set RUN_TESTS=0
set RUN_INSTALL=1
set PYTHON_VERSION=3
call scripts\build_win.cmd

echo "clCaffe compile done"

注意事项:
编译过程中会报一次错,错误为找不到caffe/proto/caffe.pb.h

这个错误不是本机编译环境的问题,而是因为这个项目还不完善,在Windows*下编译项目的顺序有些问题。在Windows*下在编译pretune_convert.vcxproj的时候,这个caffe.pb.h还没有生成。

解决办法也很简单,直接再执行一遍build-clcaffe.cmd中下图的这部分脚本即可。

::设置编译参数,开始编译clCaffe
cd %~sdp0
cd caffe
set BUILD_PYTHON=1
set BUILD_PYTHON_LAYER=1
set USE_INTEL_SPATIAL=1
set USE_GREENTEA=1
set USE_ISAAC=1
set RUN_TESTS=0
set RUN_INSTALL=1
set PYTHON_VERSION=3
call scripts\build_win.cmd

最终编译结束了

接下来要把编译出的一些动态库拷贝到caffe\build目录下,具体请参考下面的编译脚本build-install.cmd。

if not exist "%~sdp0\caffe\build\install\" (
	echo do not find caffe build
)else (

	:: copy lib and include
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\install\bin
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\install\bin
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\install\python\caffe
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\install\python\caffe
	copy /y %~sdp0\dlfcn-win32\Release\dl.dll %~sdp0\caffe\build\tools\Release
	copy /y %~sdp0\isaac\build\lib\Release\isaac.dll %~sdp0\caffe\build\tools\Release

	copy /y %~sdp0\caffe\build\libraries\lib\boost_python-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_system-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_thread-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_filesystem-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_regex-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_chrono-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_date_time-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\boost_atomic-vc140-mt-1_61.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\glog.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\gflags.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\libprotobuf.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffehdf5_hl.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffehdf5.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\caffezlib.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\lmdb.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\leveldb.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\snappy_static.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\lib\libopenblas.dll.a %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_highgui310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_videoio310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_imgcodecs310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_imgproc310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\caffe\build\libraries\x64\vc14\lib\opencv_core310.lib %~sdp0\caffe\build\install\lib
	copy /y %~sdp0\isaac\build\lib\Release\isaac.lib %~sdp0\caffe\build\install\lib

	copy /y %OPENCL_LIBRARIES% %~sdp0\caffe\build\install\lib
	copy /y %PYTHON_LIBRARY% %~sdp0\caffe\build\install\lib

	xcopy %~sdp0\caffe\build\libraries\include %~sdp0\caffe\build\install\include /s /h /c /y
	move /y %~sdp0\caffe\build\install\include\boost-1_61\boost %~sdp0\caffe\build\install\include\boost
	mkdir %~sdp0\caffe\build\install\include\viennacl
	xcopy %~sdp0\caffe\viennacl-dev\viennacl %~sdp0\caffe\build\install\include\viennacl /s /h /c /y
	mkdir  %~sdp0\caffe\build\install\include\CL
	xcopy %~sdp0\caffe\viennacl-dev\CL %~sdp0\caffe\build\install\include\CL /s /h /c /y
	mkdir  %~sdp0\caffe\build\install\include\3rdparty
	xcopy %~sdp0\caffe\include\3rdparty %~sdp0\caffe\build\install\include\3rdparty /s /h /c /y

	echo "copy done"
)

运行

运行一下clCaffe项目自带的examples里的00-classification的代码来验证一下clCaffe是否能够正常运行,我们也可以由此看到clCaffe和BVLC caffe的运行流程上的一些不同

首先在C盘的根目录下建一个clcaffe_cache的目录,然后在这个clcaffe_cache的目录下建一个viennacl的子目录。

同时在Windows*的环境变量里加入VIENNACL_CACHE_PATH = c:\clcaffe_cache\viennacl

接下来看看电脑上的可使用的GPU设备的ID号,等下运行caffe的时候需要告诉caffe用哪个GPU设备。

打开Windows* 命令行控制台应用(command console),进入clcaffe-windows\caffe\build\tools\Release目录,执行

C:\work\clcaffe-windows\caffe\build\tools\Release>caffe.exe device_query

看一下输出,

可以看到我的电脑上有2个GPU设备,
Device id:0 是Intel HD Graphics 630核显
Device id:1 是CPU模拟的GPU设备

接着针对00-classification用的caffe模型,我们先生成一下caffe运行这个model所需要cache文件。(如果事先不生产cache文件,这个cache也会在caffe第一次运行caffe model的时候自动生成,但是会导致程序第一次运行时的运行时间过长)
打开Windows* 命令行控制台应用(command console),进入clcaffe-windows\caffe\build\tools\Release目录,执行

C:\work\clcaffe-windows\caffe\build\tools\Release>caffe.exe time -gpu 0 -phase TEST --model ..\..\..\models\bvlc_reference_caffenet\deploy.prototxt
  • 这个models\bvlc_reference_caffenet\deploy.prototxt就是用到的模型文件

Caffe会基于这个models来评估一个运行这个模型最快的opencl算法,并且把评估结果写到Cache文件里去。

准备工作终于结束了,下面跑一下00-classification例子吧

打开anaconda的命令行,进入clcaffe\caffe的examples目录,运行jupyter notebook

在打开的notebook中打开caffe自带的例子 00-calssification.ipynb

这是用一个训练好的Caffe模型来预测动物图片的例子,图片默认是使用Caffe项目里examples\image\cat.jpg。

先修改第2步中caffe的路径,将路径指向clcaffe

再修改第4步中的caffe运行模式,注释掉set_mode_cpu(),加上set_device(0),set_mode_gpu(),caffe接下来会把模型放到device id为0的GPU设备上。根据前面的caffe device_query的输出,id 0为本机的核显。

一路Shift+Enter运行下去,看到第8步predicted输出 

predicted class is: 281

第9步输出

output label: n02123045 tabby, tabby cat

预测结果是猫,说明clcaffe已经正确编译而且能运行了。

大功告成。

使用clCaffe的一些注意事项

  • clCaffe只支持Gen9及以上的Intel核显,即Intel Skylake架构及以后的微处理器的核显。
  • 生成caffe cache时建议预先用无权重系数模型来生产cache,不要在caffe第一次运行你自己的代码时on-the-fly的生成cache,容易造成GPU运行出错。(这个bug正在修复中)
  • Clcaffe在创建基于GPU的net模型时,这个net会基于set_device([GPU Device ID])传进去的那个GPU device ID创建。所以接下来这个net模型无法通过set_device()来切换另一个GPU硬件,如果想切换到另一个GPU上运行,必须通过set_device([GPU Device ID])设定一个新GPU Device,再重新定义一个新Net模型。

  • Windows*下clCaffe对python支持不好,python程序在退出时会异常。在Linux下无此问题。所以建议正式代码用C++来调用caffe,同时C++接口可以定义fp16的caffe模型,获得更高的性能。

Intel clCaffe核显带来的性能提升

接下来在我的两台PC机上分别运行一下基于CPU的BVLC caffe和基于GPU加速的clCaffe,看看在日常的学习生产硬件平台上(台式机和笔记本)运行caffe,核显加速能带来多少性能的提升。测试方法为在默认的Windows*10系统且安装了常用的办公软件及开发软件的环境下(不关闭任何默认打开的后台服务),利用前面用到的caffe自带的例子 00-classification,在代码的第11步,测试net.forward()的运行时间(如下图所示)。我们基于这个测试时间来做一个简单的性能对比。

基于Intel Core i5-7440HQ移动处理器的性能测试

CPU信息

CPU版本net.forward()运行时间

GPU信息

GPU版本net.forward()运行时间

性能提升
1560ms/309ms = 5.05倍

基于Intel Core i7-6700 桌面处理器的性能测试

CPU信息

CPU版本net.forward()运行时间

GPU信息

GPU版本net.forward()运行时间

性能提升
1370ms/262ms = 5.23倍

两个测试平台数据对比分析

  • 在Caffe的CPU实现上,Caffe模型的预测时间取决于CPU的核心数量和主频率
    net.forward()时间对比
    1560ms/1370ms = 1.14倍
    CPU频率对比
    3.4GHz/2.8GHz=1.21倍 
  • 在Caffe的GPU实现上,Caffe模型的预测时间主要取决于Net模型的复杂度和GPU的主频。
    net.forward()时间对比
    309ms/262ms = 1.18倍
    GPU频率对比
    1.15GHz/1GHz=1.15倍
  • 相同硬件平台上CPU实现和GPU实现对比,GPU版本的处理速度领先于CPU版本5倍以上
    net.forward()时间对比
    平台1:1560ms/309ms = 5.05倍
    平台2:1370ms/262ms = 5.23倍

结论

在人工智能领域,利用Intel核显GPU做硬件加速,在Caffe上做图像预测(Inference)时可以带来比纯CPU版本Caffe高达5倍以上的性能提升。这种使用场景特别适合使用Intel的低端桌面处理器,移动处理器,以及凌动处理器平台的IOT设备、Edge设备及家庭电脑上,在这种低功耗、低CPU性能的情况下可以利用Intel集成GPU大大提高这些硬件平台的AI预测速度。

后记

本文介绍的clCaffe并没有获得其最佳性能。要想让clCaffe获得最佳性能,我们还需要对模型进行优化(clCaffe采用的是模型融合),并采用FP16来进行推理。

模型融合(Model Fusion)意思是说在神经网络内,一些层可以合并在一起计算。通常情况下,我们可以将BatchNorm,Scale,Relu层合并进入Conv层。模型融合的好处是降低了数据读取的次数,因为在推理过程中除了运算,数据读写也占用了大量的时间。

FP16也称之为半精度浮点,一般浮点数为4字节(FP32),FP16顾名思义为2字节,大部分现代GPU中设计了FP16的运算单元, 相对FP32可以获得1.3~2倍数的性能提升。在clCaffe中可以创建Half类型的网络, 当这样的网络加载FP32的模型时,内部会自动转换成FP16的模型进行计算,速度可以进一步提升(但是,目前只能使用c/c++代码才能创建FP16网络)。

有关clCaffe的模型融合以及FP16推理相关的内容,将会在下一篇博文介绍。你也可以在这里https://github.com/liyuming1978/caffe_example找到更多的如何更好使用clCaffe的相关信息。

基于 CPU 实施高效且快速的医疗影像分析

$
0
0

基于 2017 年阿里巴巴天池医疗 AI 大赛的实验性总结

作者:胡潇吴慧姚伟峰

概述

本文基于由阿里云、英特尔和零氪科技联合主办的天池医疗 AI 大赛。在大赛中选手挑战通过计算机断层 (CT) 扫描自动检测肺结节。该赛事吸引了全球 2,887 支参赛队伍,经过 7 个月的紧张角逐,在 2017 年 10 月成功落下帷幕。大赛托管于阿里巴巴的公有云服务,此服务完全基于英特尔的深度学习软硬件堆栈而构建。在整个比赛过程中,英特尔积极参与了架构设计、软硬件开发、性能优化和在线支持,因此积累了有关医疗 AI 的丰富洞察。本文将以实验方式阐述这些重要发现。

首先,我们吸收了所有天池参赛者共同的设计原则,实现了一个具有代表性的 3D 卷积神经网络 (CNN) 模型,以反映当前最新的肺结节检测水平。

第二,我们运用分辨率不同的输入数据训练该模型,并以定量的方式证明,用分辨率较高的数据训练的模型可有效提升检测性能,尤其是针对小结节的检测性能。但同时,高分辨率模型所使用的内存比低分辨率模型多。

第三,我们对通用图形处理器 (GPGPU) 和 CPU 的行为进行了比较,并证明 CPU 架构可提供更大的内存容量,因此有助于医疗 AI 开发人员探索高分辨率设计,从而实现最佳检测性能。

我们还引进了定制的深度学习框架,名为“ Extended-Caffe*”[10],它是天池软件堆栈的核心,我们以此为例有效地证明了 CPU 架构可支持快速 3D CNN 计算,进而方便人们高效且快速地在CPU上进行3D CNN 模型开发。

背景

天池医疗 AI 大赛 [1]是中国首次举办的 AI 医疗大赛,其规模和数据量在世界范围内均属首例。中国 16 家顶尖肿瘤医院为大赛提供近 3,000 例患者的肺部 CT 扫描数据。之所以选择肺结节检测为主题,是因为过去 30 年以来,中国肺癌患病率逐年上升,已经成为癌症中死亡率最高的疾病。因此,解决肺结节早期筛查问题尤为重要,刻不容缓。全球 2,887 支参赛队伍经过为期 7 个月的紧张比赛,最终北京大学团队脱颖而出,获得冠军。

在线比赛托管于阿里巴巴的公有云服务,此服务完全基于英特尔的深度学习软硬件堆栈而构建。基础硬件设施为基于英特尔® 至强® 和英特尔® 至强融核™ 处理器的平台集群,该平台提供总共 400+ TFLOPS 的计算能力。英特尔还提供一系列深度学习软件组件支持模型训练和推理,其核心是定制的深度学习框架,称为 Extended-Caffe,专为医疗 AI 用途而优化。英特尔专家还帮助数百位在线参赛者高效运行模型,在此过程中积累了有关医疗 AI 领域的重要洞察。

此次大赛表明,尽管深度学习应用于计算机视觉领域已超过十年,但对于领域专家和工程师来说,医疗影像分析仍然面临着严峻的挑战。特别是几乎所有最先进的医学影像分析解决方案都严重依赖于 3D甚至 4D/5D CNN,相比于计算机视觉领域的其他常见 2D CNN,它们在工程设计方面的考虑因素有很大的不同。我们发现,相比于传统 GPGPU,CPU 平台可更有效地支持使用 3D CNN 进行医疗影像分析,因为 CPU 在内存容量方面具有显著的优势。而同时,通过巧妙的底层算法设计,CPU平台可以支持快速的3D CNN元语操作,以保证模型训练和推理的速度。

尽管天池数据集和模型不对外公开,但我们自主研发和试验了一个 3D CNN 模型,以反映当前最新的肺结节检测结果,以此阐述我们的重要结论。下文将逐一介绍我们如何预处理 CT 数据集、如何设计模型、在 CPU 上如何实现高速 3D元语操作、以及实验步骤,并通过定量分析得出结论。

CT 数据预处理

原始 CT 影像包含一系列 2D 切片。相邻2D 切片之间的间隔称为Z间隔。每张2D切片都是一个灰度像素矩阵,其中像素之间的水平间隔和垂直间隔分别称为 X 间隔和 Y 间隔。原始的CT影像数据均记录这些间隔的实际长度(以毫米为单位)。由于 CT 仪器之间经常存在差异,因此不同的原始 CT 影像其间隔也各不相同。例如,在 LUNA'16 数据集 [3]中,Z 间隔从 0.625 毫米到 2.5 毫米不等。天池数据集也存在类似的情况。为了使深度学习模型所处理的数据集保持统一,我们必须以固定的采样距离,在 X、Y 和 Z 三个方向对原始像素进行插值,从而使原始 CT数据 转换为新的3D图像,其中3 个方向的像素间隔均等于该采样距离。然后,以像素为单位进行测量,新的3D图像和结节的大小(即分辨率)将取决于该采样距离。表 1 显示较小的采样距离可提高分辨率。注意,与检测普通物体不同,检测肺结节时遇到的特有问题是,结节仅占整个 CT体积的百万分之一。因此,为了使模型能有效提取结节的特征,我们必须将 CT 图像裁剪成较小的 3D 区域,然后将这些裁剪区逐一导入模型中进行训练。同样,较小的采样距离对应较大的裁剪区。

表 1.不同的采样距离导致分辨率不同的 3D 数据

采样距离 (毫米)3D 图像分辨率 (像素 x 像素 x 像素)结节分辨率 (直径:像素)裁剪区分辨率 (像素 x 像素 x 像素)
1.00249x256x3023.66128x128x128
1.33188x196x2312.7496x96x96
2.00127x136x1591.8364x64x64

用于肺结节检测的 3D CNN 模型

图 1.我们的 3D CNN 模型架构(以输入的裁剪区大小 = 128 x 128 x 128为例)

根据业界其他成果 [4‒6]和天池大赛中的各种模型,参考他们的共同原则,我们构建了一个用于肺结节检测的 3D CNN 模型,如图 1 所示,该模型分为下采样和上采样两部分。下采样部分由 5 个 3D 残差块与 4 个池化层相互交织而成。残差块由卷积、批量归一化、ReLU 和其他操作组成,另外还有一个残差结构(图 1 中的 C1 和 C2)。上采样过程通过两个反卷积(图1中的 Deconv)来完成。我们将每个反卷积的输出和相应下采样层的输出进行合并,使所得的特征图包含原始输入数据的局部和全局信息。

对于每个输入裁剪区 (m x m x m),我们的模型生成 (m/4) x (m/4) x (m/4) x 3 个立方体区域框,称为“候选区”。为每个候选区会生成一个概率值(即该候选区内包含结节的可能性),一组坐标(用以表征该候选区的中心点),以及该候选区的大小(即边长)。

通常,在3D CNN模型之后,仍需要进行一系列的后处理,包括假阳性剔除及一些其他步骤,用以进一步筛选出真阳性候选区。不过,由于3D CNN模型本身的有效性和速度是工程实施的决定性因素,因此,本文没有关注这些步骤。但是,即便没有辅以这些后处理步骤,我们所训练的模型(CCELargeCubeCnn [9])依然在LUNA’16排行榜中排名第 14位,这足以说明我们的这一实验模型的确代表了当前最先进的肺结节检测水平。

CPU 上的高效 3D CNN 元语操作

我们在实验中通过使用不同的分辨率和超参数(例如批处理规模)运行模型,对比 GPGPU 和 CPU 平台的有效性。因此,必须首先确保在 CPU 平台上的运算速度,特别是针对最常用的3D 卷积元语操作。

CPU 上的 3D 卷积元语操作

我们利用面向深度神经网络的英特尔® 数学核心函数库(英特尔® MKL-DNN [7])中高度优化的 2D 卷积元语,在 CPU 上实现了高效的 3D 卷积元语(见图 2)。首先,我们将 3D 数据和卷积核分别视为一组 2D 切片。然后,一个3D 卷积运算就等价于先对相应的 2D 切片(图 2 中颜色相同的切片)分别进行卷积操作,然后再将所有的中间结果相加。由于英特尔® MKL-DNN的 2D 卷积元语在 CPU 上已经经过了高度的优化,因此我们的 3D 卷积也可以在 CPU 上高速运行。

图 2.快速 3D 卷积元语的实现原理(调用MKL-DNN的2D卷积元语)

为了显示效果,我们还开发了另一个基准实现,称为基于普通矩阵乘法 (GEMM) 的 3D 卷积。它是一个简单直接的实现。图 3 描述了一个基于GEMM 的2D卷积算法的工作原理,即重新排列数据和卷积核,然后使用矩阵乘法计算出卷积结果。而在这里,我们运用相同的方法处理 3D 数据和卷积核,就得到了基于GEMM 的3D卷积。因为这种算法的核心运算是矩阵乘法,对此我们可以利用英特尔® 数学核心函数库 (英特尔® MKL) [8]中高度优化的 SGEMM 来实现。也就是说,我们的这一基准实现实际上已经可以在 CPU 上达到较高的速度。

图 3.基于 GEMM 的 2D 卷积(利用英特尔® 数学核心函数库 SGEMM)

而图 4 则显示了我们实现的快速 3D 卷积元语的速度仍大大高于基于 GEMM 的3D卷积元语基准实现:前向路径的速度提升了 4 倍,后向提升了 30%,而整体速度提升了 2 倍。

图 4.执行时间比较(快速 3D 卷积元语实现对比于基于 GEMM 的 3D 卷积元语基准实现)

CPU 上的其他 3D CNN 元语操作

除了 3D 卷积元语以外,还必须确保所有相关 3D元语的速度。得益于英特尔® MKL 和英特尔® MKL-DNN,我们实现了一系列CPU上的快速 3D 元语操作,包括 3D 批量归一化、3D 反卷积、3D 池化、3D SoftMax 损失、3D 交叉熵损失、3D smooth L1 损失、3D concat 等。这些成果已经发布到Extended-Caffe [10],并成为天池大赛软件堆栈的核心。图 5 显示了整体模型的训练和推理速度的提升。

图 5.整体效率提升
(优化前、后对比)。
训练耗时按照一次迭代来测量(即处理一个裁剪区的时间),而推理耗时按照一整张CT 图像来测量(即处理一幅完整的CT 图像中所有裁剪区的时间)。

实验结果和定量分析

模型训练

我们采用随机梯度下降(SGD)和逐步学习率策略来训练模型。所有网络参数都进行随机初始化,初始学习率设为 0.01。模型训练 100 次,并分别将第 50 次和第 80 次的学习率缩小 10 倍。图 6 记录了我们训练 LUNA'16 数据集(例如子集 0)时的损失趋势。可以看到,在第 80 至第 100 轮训练期间,模型在该数据集上成功收敛。

图 6.模型训练示例(LUNA’16 subset 0)

检测性能评估

天池、LUNA'16 等知名大赛都采用 FROC(自由响应接收器操作特性)[11]方法来评估模型的检测性能。如果某一候选区(即模型最终输出的立方体区域框)的中心点与实际结节的中心点的距离比结节半径短,那么该候选区将被视作真阳性。然后,计算 FROC 得分以综合评价该模型的敏感性与假阳性误差。

分辨率的影响

图 7 显示了 3D CNN 模型的不同 FROC 得分与用于模型训练的输入数据的不同分辨率之间的关系。可以看到,分辨率较高时训练模型的 FROC 得分较高。也就是说,实验证明,高分辨率有助于提高模型的检测性能。

图 7.分辨率越高,FROC 得分越高

由于人类放射科医生能够轻易检测到大结节,而很难检测到小结节,因此急需 AI 辅助检测方案具备高效检测小结节的能力。图 8 比较了我们的模型在不同分辨率下检测不同尺寸的结节的效果。可以看到,高分辨率可显著提高小结节的检测性能。

图 8.高分辨率可提高检测小结节的精确度

内存使用分析

在不同分辨率下训练模型的过程中,我们分析了内存使用情况,并对比了 CPU 平台和 GPGPU 平台的行为。图 9 (a) 和 (b) 记录了训练批量大小(batch size)分别等于 1 和 4 的情况。当批量大小等于 1 时,具备 12GB 内存的现代 GPGPU 仅支持最高 128 x 128 x 128 的分辨率,而包含 384GB 内存 CPU 平台可轻松支持高达 448 x 448 x 448 的分辨率。而当批量大小等于 4 时,该GPGPU 的情况则更糟 — 仅支持最高 96 x 96 x 96 的分辨率,而 上述CPU 平台可轻松支持高达 256 x 256 x 256 的分辨率。


(a) 批量大小=1


(b) 批量大小=4

图 9. 内存使用与不同分辨率

由于现代 CPU 服务器拥有数 TB 内存容量,而且考虑到英特尔即将推出Apache Pass 技术,因此可以说, CPU 平台能提供近乎无限的灵活性,用以支持医学影像分析领域的模型设计人员探索分辨率极高的解决方案,从而获取最佳检测性能。

总结

由阿里云、英特尔和零氪科技联合主办的天池医疗 AI 大赛,其环境是一套完全基于英特尔深度学习软硬件堆栈构建而成的AI云计算服务。本文从天池大赛出发,以定量实验的方式阐述了我们在医学 AI 领域所获得的重要结论。首先,我们自主研发了一个可反映当前最新肺结节检测水平的 3D CNN 模型。接下来,我们通过定量方式证明了,用高分辨率数据训练的模型可显著提升检测性能,特别是检测小结节的精确度。同时,高分辨率模型必须消耗更多内存。我们对比了 GPGPU 和 CPU,并证明 CPU 平台由于能够提供较大的内存容量,有助于医疗 AI 设计人员探索更高分辨率的解决方案,进而实现最佳的检测性能。工程上,通过实现 Extended-Caffe 框架作为天池大赛的核心软件,以此为例,证明了 CPU 架构可以支持快速 3D CNN 运算,从而保证了医疗影像分析领域的开发者在CPU上真正高效且快速的研发3D CNN 模型。

参考

  1.  https://tianchi.aliyun.com/getStart/introduction.htm?raceId=231601
  2. Bush I., Lung nodule detection and classification. Technical report, Stanford Computer Science, 2016.
  3. https://luna16.grand-challenge.org/home/
  4. Girshick, R. Fast R-CNN. Computer Science, 2015.
  5. Ronneberger, O., Fischer, P., and Brox, T. U-Net: Convolutional Networks for Biomedical Image Segmentation, Medical Image Computing and Computer-Assisted Intervention — MICCAI 2015. Springer International Publishing, 2015:234‒241.
  6. https://github.com/lfz/DSB2017
  7. https://software.intel.com/zh-cn/articles/intel-mkl-dnn-part-1-library-overview-and-installation
  8. https://software.intel.com/zh-cn/mkl
  9. https://luna16.grand-challenge.org/results/
  10. https://github.com/extendedcaffe/extended-caffe
  11. http://devchakraborty.com/Receiver%20operating%20characteristic.pdf

借助英特尔® 优化 Chainer* 管理深度学习网络

$
0
0

总结

Chainer 是基于 Python 的深度学习框架,旨在提高灵活性和直观性。它可根据 define-by-run 方法(亦称动态计算图)提供自动差异化 API,并提供面向对象的高级别 API,以构建和训练神经网络。它支持各种网络架构,包括前馈网络、卷积网络、循环网络和递归网络。它还支持 per-batch 架构。正演计算可包含 Python 的所有控制流声明,完全具备反向传播能力。它可使代码更加直观,并易于调试。英特尔® 优化 Chainer* 目前集成了最新版面向深度神经网络的英特尔® 数学核心函数库 (英特尔® MKL-DNN) 2017,专门面向高级矢量扩展指令集 AVX-2 和 AVX-512 指令优化,英特尔® 至强® 处理器和英特尔® 至强融核™ 处理器等支持这些指令集。

推荐环境

我们建议使用这些分发版 Linux。

  • Ubuntu 14.04/16.04 LTS 64 位
  • CentOS 7 64 位

可使用以下 Python 版本:

  • 2.7.5+、3.5.2+ 和 3.6.0+

上述建议使用的环境经过测试。我们无法保证英特尔® 优化 Chainer* 适用于其他环境,包括 Windows 和 macOS,即使英特尔® 优化 Chainer* 运行看似正确。

关联组件

如果使用的是旧版设置工具,我们建议在安装英特尔® 优化 Chainer* 之前进行升级:

$ pip install -U setuptools

安装英特尔® 优化 Chainer* 时需要使用以下软件包。

  • NumPy 1.9, 1.10, 1.11, 1.12, 1.13
  • Six 1.9+
  • Swig 3.0.9+

映像数据集支持

  • pillow 2.3+

HDF5 序列化支持

  • h5py 2.5+

测试实用程序

  • pytest 3.2.5+

英特尔® MKL-DNN

  • 大家无需手动安装英特尔® MKL-DNN,安装英特尔® 优化 Chainer* 时,系统将自动下载和构建英特尔® MKL-DNN,因此也需要使用 boost、glog 和 gflags。

安装

大家可以使用 setup.py 通过 tarball 安装英特尔® 优化 Chainer*:

$ python setup.py install

使用 pip 卸载 Chainer:

$ pip uninstall chainer

 

使用 Docker 运行

我们分别根据 python2 和 python3 提供面向 Ubuntu 和 Centos 的 Docker 映像和 Dockerfile。详情请见如何构建和运行英特尔® 优化 Chainer* Docker 映像

 

培训示例

使用 mnist 数据集训练测试:

$ cd examples/mnist
$ python train_mnist.py -g -1

使用 cifar 数据集训练测试:

  • 运行 CIFAR-100 数据集:
$ cd examples/cifar
$ python train_cifar.py –g -1 --dataset='cifar100'
  • 运行 CIFAR-10 数据集:
$ cd examples/cifar
$ python train_cifar.py –g -1 --dataset='cifar10'

 

单节点性能测试配置

单节点性能测试配置请查看https://github.com/intel/chainer/wiki/Intel-Chainer-Single-Node-Performance-Test-Configurations

许可证

MIT 许可证(参考许可证文件)。

参考文献

Tokui, S., Oono, K., Hido, S. and Clayton, J., Chainer: a Next-Generation Open Source Framework for Deep Learning, Proceedings of Workshop on Machine Learning Systems(LearningSys) in The Twenty-ninth Annual Conference on Neural Information Processing Systems (NIPS), (2015) URLBibTex

 

更多信息

 

 

在Windows上的Caffe实战:猫狗大战

$
0
0

作者:Qian, Caihong

文档目的

本文以Kaggle的猫狗大战为例,介绍在Windows上如何利用Caffe对自己的图片进行训练以及进行简单的调参,并且利用训练好的模型进行测试和分类预测。

环境介绍

本文所述的工具和命令适用Windows+BVLC Caffe的CPU或GPU版本 (需要在提前机器上安装BVLC Caffe并成功编译); 以及Windows+ clCaffe的版本, 但clCaffe是基于Intel Skylake及以后的处理器核显做硬件加速的修改版,使用时要注意。

准备原始数据

原始数据即用来做训练的数据,数据可以是自己制作的,也可以在网上找现成的。这里使用的是kaggle的dogsvscats(猫狗大战)的图片,下载地址:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data

猫狗大战的数据集来自kaggle上的一个竞赛,它是一个简单的二分类的例子,里面训练集有25000张图片,猫狗各占一半,训练集图片名字中带有标签信息;测试集有12500张图片,图片名称中无标签信息。项目的目的是用训练好的神经网络识别给出的图片是猫还是狗。

下载下来的数据内容如下:

我们从训练集(25000张)中选出5000张图片(猫狗各一半)作为验证集,验证集是用来验证模型的准确度,所以需要使用带标签的数据。注意在Caffe的很多文档中,验证集被直接称为测试集,但在这里验证集是指我们分出来的5000张带标签的图片,而不是测试集那12500张不带标签的图片,本文中主要用到训练集和验证集的数据。有的时候训练集和验证集也可以被用来作为测试集。

我们最后得到的数据集目录如下:

转换图像格式

为了更高效地读取数据,Caffe要求将待训练和验证的图片转换数据格式:leveldb或lmdb格式。这里使用的是caffe自带的convert_imageset.exe 工具来进行图片格式的转换,这个工具在caffe成功编译后会生成,位置在caffe\build\tools\Release 下。

这个工具的参数如下:

  convert_imageset [FLAGS] ROOTFOLDER LISTFILE DB_NAME

参数解析:

  ROOTFOLDER:表示需要处理的图像集的根目录;

  LISTFILE表示输入的图像文件列表,其每一行代表图像集中的每一个图样的路径和相应的标注,比如

上图“train/578.jpg 5”代表当前路径下的/train目录下的578.jpg这张图片的标签为5.

DB_NAME:为要生成的数据库的名字。

[FLAGS]:为可选参数, 可以指是否使用shuffle,颜色空间,编码等

可选参数FLAGS设置

  • gray:bool类型,默认为false;如果设置为true,则代表将图像当做灰度图像来处理,否则当做彩色图像来处理。
  • shuffle:bool类型,默认为false,如果设置为true,则代表将图像集中的图像的顺序随机打乱。一般来说,为防止出现过分有规律的数据,导致训练结果overfit,我们都会讲训练数据打乱。
  • backend:string类型,可取的值的集合为{"lmdb", "leveldb"},默认为"lmdb",代表采用何种形式来存储转换后的数据
  • resize_width:int32的类型,默认值为0,如果为非0值,则代表图像的宽度将被resize成resize_width
  • resize_height:int32的类型,默认值为0,如果为非0值,则代表图像的高度将被resize成resize_height
  • check_size:bool类型,默认值为false,如果该值为true,则在处理数据的时候将检查每一条数据的大小是否相同
  • encoded:bool类型,默认值为false,如果为true,代表将存储编码后的图像,具体采用的编码方式由参数encode_type指定
  • encode_type:string类型,默认值为"",用于指定用何种编码方式存储编码后的图像,取值为编码方式的后缀(如'png','jpg',...)

举个例子:convert_imageset --gray=true --resize_width=160 --resize_height=160  ImgSetRootDir ImgFileList.txt imgSet.lmdb

所以,需要得到我们要的数据格式,需要以下几步:

  1. 先生成LISTFILE,我们使用cmd命令读取目录内全部文件的文件名并输出到文件中,命令格式:

    dir target_folder /b > output_filelist

    在本例中,提取..\dogsvscats\train 下的所以文件的文件名,并输出到filelist.txt中:

    然后将输出文件中的关键字批量替换即可。

    本例中的training数据最后生成的train_list.txt内容如下图。其中0代表cat, 1代表 dog:

    同样的方法生成val_list.txt

    注意:标签一定要从0开始,中间数字要连续,不能随便乱设。

  2. 新建一个convertdata.bat文件,将convert_imageset.exe写进去:

    由于原始的图片大小不一致,所以这里需要将图片resize 一下,全部resize为208x208.

    格式不指定的话默认输出为lmdb格式。

    如果需要改成leveldb格式的话需要增加flag参数”--backend=leveldb”。这里我们使用的是lmdb格式。

  3. 运行这个bat,在几分钟之后就生成lmdb数据成功了:

  4. 在当前目录下查看生成的lmdb文件:

    里面生成的文件是这样的:

    如果转换的是leveldb格式的话,数据是这样的:

  5. 到此,需要的train和val数据文件都准备好了

计算图像均值

图像的均值就是计算所有训练样本的平均值,计算出来后保存为一个平均值。图片减去这个均值后再进行训练和测试,这样会提高训练的速度和精度。

这里也是使用Caffe自带的计算均值的工具compute_image_mean.exe,这个工具在caffe成功编译后会生成,位置在caffe\build\tools\Release 下。

功能:计算训练数据库的平均图像,因为平均归一化训练图像会对结果有提升。

使用该工具的命令行格式如下:

  compute_image_mean [FLAGS] INPUT_DB  OUTPUT_FILE

参数解析:

  1. INPUT_DB: 数据源:(LevelDB或者LMDB),图像必须事先转换成LEVELDB或LMDB格式,才能到这一步
  2. FLAGS参数:可选,参考上面的解释。默认为LMDB格式;但如果处理的是LEVELDB格式,必须添加flag参数”--backend=leveldb”
  3. OUTPUT_FILE: 计算出来结果输出文件名,不提供的话,不保存平均图像blob

还是新建一个image_mean.bat文件,输入如下命令:

运行这个bat,会在当前路径生成所需要的均值文件dogsvscats.mean.binaryproto

这里,验证集不需要再一次计算均值文件,而是直接使用训练集得到的均值文件。

创建模型文件

Caffe和核心文件是*_solver.prototxt,这个文件定义了模型的迭代次数,使用的算法和参数更新的策略等;

另一个文件是*_train_test.prototxt, 这个文件是训练和验证的配置文件,在*_solver.prototxt中调用它。当然,train和test阶段也可以分开配置。

这里我们将基于caffe自带的CIFAR10 examples下的cifar10_quick_solver.prototxt和cifar10_quick_train_test.prototxt进行修改。先将这2个文件复制到我们自己的目录下,将文件名相应修改为dogsvscats_solver.prototxt和dogsvscats_train_val.prototxt。

  1. dogsvscats_solver.prototxt修改以后的内容以及相关的解释:

    # The train/val net protocol buffer definition
    
    net: "dogsvscats_train_val.prototxt"	#
    
    //定义网络结构文件为“dogsvscats_train_val. Prototxt”,每一个模型就是一个net,需要在一个专门的配置文件中对net进行配置,每个net由许多的layer所组成。每一个layer的具体配置方式在下文中参见。要注意的是:文件的路径要从dogsvscats_solver.prototxt所在目录开始,其它的所有配置都是这样。
    
    # test_iter specifies how many forward passes the test should carry out.
    
    # In the case of MNIST, we have test batch size 100 and 100 test iterations,
    
    # covering the full 5,000 testing images.
    
    test_iter: 100	#
    
    //这个要与test layer中的batch_size结合起来理解,注意这个数字其实只跟验证集的图片数量有关而跟训练集的图片数量无关。mnist数据中测试样本总数为10000,它是分了100个iter每个batch_size为100来全部执行完。但本例中我们有5000个验证集样本,所以分了100次迭代,每次迭代的batch_size为50,这样100次迭代后正好处理了5000个样本.
    
    # Carry out testing every 500 training iterations.
    
    test_interval:500	#
    
    //测试间隔。也就是每训练500次,才进行一次测试(确切说应该叫验证)
    
    # The base learning rate, momentum and the weight decay of the network.
    
    base_lr: 0.001		#
    
    // base_lr用于设置基础学习率,这是一个非常重要的参数,在迭代的过程中,可以对基础学习率进行调整。怎么样进行调整,是由lr_policy来设置。
    
    momentum: 0.9	#
    
    //上一次梯度更新的权重
    
    weight_decay: 0.0005	#
    
    //权重衰减系数,防止过拟合的一个参数
    
    # The learning rate policy
    
    lr_policy: "inv"		#
    
    //设置学习率的相关优化策略。lr_policy可以设置为下面这些值,相应的学习率的策略为:
    
    •	- fixed: 保持base_lr不变.
    
    •	- step: 如果设置为step,则还需要设置一个stepsize,  返回 base_lr * gamma ^ (floor(iter / stepsize)),其中iter表示当前的迭代次数
    
    •	- exp: 返回base_lr * gamma ^ iter, iter为当前迭代次数
    
    •	- inv: 如果设置为inv,还需要设置一个power, 返回base_lr * (1 + gamma * iter) ^ (- power)
    
    •	- multistep: 如果设置为multistep,则还需要设置一个stepvalue。这个参数和step很相似,step是均匀等间隔变化,而multistep则是根据stepvalue值变化
    
    •	- poly: 学习率进行多项式误差, 返回 base_lr (1 - iter/max_iter) ^ (power)
    
    •	- sigmoid: 学习率进行sigmod衰减,返回 base_lr ( 1/(1 + exp(-gamma * (iter - stepsize))))
    
    gamma: 0.0001 	#//上面介绍计算base_lr要用到
    
    power: 0.75	#//上面介绍计算base_lr要用到
    
    # Display every 100 iterations
    
    display: 100	#
    
    //每训练100次,在屏幕上显示一次相关信息。如果设置为0,则不显示。
    
    # The maximum number of iterations
    
    max_iter: 10000	#
    
    //最大迭代次数 。这个数如果设置太小,会导致没有收敛,精确度很低。设置太大,会导致震荡,浪费时间。所以在实际训练时要根据情况进行调整
    
    # snapshot intermediate results
    
    snapshot: 5000		#
    
    //每迭代5000次,保存一次结果
    
    snapshot_prefix: "stored_model"	#
    
    //保存结果路径和名称。将训练出来的model和solver状态进行保存,snapshot用于设置训练多少次后进行保存,默认为0,则不保存。
    
    # solver mode: CPU or GPU
    
    solver_mode: CPU	#
    
    //设置运行模式为CPU还是GPU。默认为GPU,如果你没有GPU,则需要改成CPU,否则会出错。在配置了GPU的PC上只要把这个参数改成GPU即可实现GPU训练
    
  2. dogsvscats_train_val.prototxt的内容以及相关的解释:
    name: "Dogsvscats"		#//该文件的名称,可随意取名
    layer {
      name: "dog"			#//layer名
     		 type: "Data"			#//这一层的类型为Data
      top: "data"		#//一般用bottom表示输入,top表示输出,多个top表示有多个输出
      top: "label"
      include {
        phase: TRAIN	#//include表示此层只属于TRAIN阶段
      }
      transform_param {
        mean_file: "dogsvscats.mean.binaryproto"		#//均值文件路径
      }
      data_param {
        source: "train_imgSet.lmdb"		#//训练数据图片路径,如果路径不同,要加上路径
        batch_size: 50				#//一个批次采用的图片数量
        backend: LMDB			#//数据格式。如果不写,则默认为LEVELDB
      }
    }
    layer {
      name: "dog"
      type: "Data"
      top: "data"
      top: "label"
      include {
        phase: TEST		#//TEST阶段(其实是验证阶段)
      }
      transform_param {
        mean_file: "dogsvscats.mean.binaryproto"
      }
      data_param {
        source: "val_imgSet.lmdb"
        batch_size: 50
        backend: LMDB
      }
    }
    layer {
      name: "conv1"
      type: "Convolution"		#//type为Convolution代表此layer为卷积层
      bottom: "data"			#//bottom代表这一层的上一层为data
      top: "conv1"
      param {
        lr_mult: 1				#//第一个表示权值的学习率
      }
      param {
        lr_mult: 2		#//第二个表示偏置项的学习率,一般偏置项的学习率是权值学习率的两倍。
      }
      convolution_param {
        num_output: 32		#//卷积核(filter)的个数
        pad: 2				#//pad设置为2,则四个边缘都扩充2个像素,即宽度和高度都扩充了4个像素
        kernel_size: 3			#//卷积核的大小为3x3
        stride: 1				#//卷积核每次移动的步长
        weight_filler {
          type: "gaussian"	#//权值初始化,默认为“constant",值全为0,很多时候我们用"xavier"算法来进行初始化,也可以设置为”gaussian"
          std: 0.0001			#//高斯分布的方差,std越小,证明高斯曲线越平滑
        }
        bias_filler {
          type: "constant"		#//偏置初始化为常数,0
        }
      }
    }
    layer {
      name: "pool1"
      type: "Pooling"			#//这一层为池化层
      bottom: "conv1"
      top: "pool1"
      pooling_param {
        pool: MAX				#//采用最大池化。平均值法的话设为AVE
        	    kernel_size: 3			#///池化的kernel为3x3
        stride: 2				#//池化的步长,默认为1。一般设置为2,即不重叠
      }
    }
    layer {
      name: "relu1"
      type: "ReLU"				#//激活层
      bottom: "pool1"
      top: "pool1"
    }
    layer {
      name: "conv2"
      type: "Convolution"
      bottom: "pool1"
      top: "conv2"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
      convolution_param {
        num_output: 32
        pad: 2
        kernel_size: 5
        stride: 1
        weight_filler {
          type: "gaussian"
          std: 0.01
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layer {
      name: "relu2"
      type: "ReLU"
      bottom: "conv2"
      top: "conv2"
    }
    layer {
      name: "pool2"
      type: "Pooling"
      bottom: "conv2"
      top: "pool2"
      pooling_param {
        pool: AVE
        kernel_size: 3
        stride: 2
      }
    }
    layer {
      name: "conv3"
      type: "Convolution"
      bottom: "pool2"
      top: "conv3"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
      convolution_param {
        num_output: 32
        pad: 2
        kernel_size: 5
        stride: 1
        weight_filler {
          type: "gaussian"
          std: 0.01
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layer {
      name: "relu3"
      type: "ReLU"
      bottom: "conv3"
      top: "conv3"
    }
    layer {
      name: "pool3"
      type: "Pooling"
      bottom: "conv3"
      top: "pool3"
      pooling_param {
        pool: AVE
        kernel_size: 3
        stride: 2
      }
    }
    layer {
      name: "ip1"
      type: "InnerProduct"		#//第1个全连接层
      bottom: "pool3"
      top: "ip1"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
      inner_product_param {
        num_output: 16		#//输出为16
        weight_filler {
          type: "gaussian"
          std: 0.1
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layer {
      name: "ip2"
      type: "InnerProduct"		#//第2个全连接层
      bottom: "ip1"
      top: "ip2"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
      inner_product_param {
        num_output: 32
        weight_filler {
          type: "gaussian"
          std: 0.1
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layer {
      name: "ip3"
      type: "InnerProduct"		#//第3个全连接层
      bottom: "ip2"
      top: "ip3"
      param {
        lr_mult: 1
      }
      param {
        lr_mult: 2
      }
      inner_product_param {
        num_output: 2		#//作为一个2分类问题,此处作为最后一层必须输出为2
        weight_filler {
          type: "gaussian"
          std: 0.1
        }
        bias_filler {
          type: "constant"
        }
      }
    }
    layer {
      name: "accuracy"
      type: "Accuracy"			#//输出分类(预测)精确度,只有TEST阶段才有,因此需要加入include参数
      bottom: "ip3"
      bottom: "label"
      top: "accuracy"
      include {
        phase: TEST
      }
    }
    layer {
      name: "loss"
      type: "SoftmaxWithLoss"
      bottom: "ip3"
      bottom: "label"
      top: "loss"
    }

开始训练

在dogsvscats_solver.prototxt和dogsvscats_train_val.prototxt同级目录下创建run_training.bat运行训练的命令如下:

执行这个bat文件即开始运行训练。

最后训练在达到max_iter后完成。在这个模型里最后得到的准确度为0.8812。 想要提高精度的话,需要调整模型参数,或者增加训练数据,可能需要尝试多次,此文暂不涉及。

测试模型

对于已训练好的模型(或者下载得到的模型),如果想测试一下模型的准确度,可以使用命令caffe test + 模型参数,如下:

解析:

caffe.exe test #//表示只做预测,不进行参数更新

--model=dogsvscats_train_val2.prototxt     #//指定模型描述文件

-weights=stored_model_iter_10000.caffemodel  #//指定预先训练好的模型权值文件

-iterations 100       #//指定测试迭代次数。参与测试样例数目 = 迭代次数 * batch_size

读取已训练的模型追加训练次数

假设当前已存储下来的训练好的模型为:

现在想要在这个模型基础上再追加训练2000次,那么,先修改solver文件到合适的max_iter(增加2000),

接着,在新建的bat文件中输入以下命令,执行这个bat则会在stored_model_iter_15000的基础上继续训练。

分类预测

前面VAL目录下的数据只是用来训练过程中的验证,它们是本身就设置了标签的,这里的测试数据是指没有标签数据,纯粹由训练好的模型来预测类别结果。

Caffe提供了一个分类工具classification.exe,位置在\build\examples\cpp_classification\Release 下:

注意这个工具只能对单个图片进行分类预测,如果需要批量预测,需要自行修改工具。

运行这个classification这个命令需要提供5个输入:1)deploy.prototxt文件;2)训练好的model文件;3)mean file; 4)label file; 5)待预测的图片。

首先要准备deploy文件,这个文件可以由网络文件“dogsvscats_train_val.prototxt”修改而来(官方参考: https://github.com/BVLC/caffe/wiki/Using-a-Trained-Network:-Deploy )。其内容和*_train_val.prototxt文件内容比较类似,但不同的是后者是用于训练目的,而它是用于网络完成训练之后。打开“dogsvscats_train_val.prototxt”,改名为“dogsvscats.prototxt”进行修改:

  1. 删除TEST部分和Accuracy部分的内容

  2. 修改train部分的输入数据,修改前后的内容如下:

  3. 最后一层的loss改为prob

Deploy文件完成之后,再准备一个label文件,取名为”result.txt”,内容是输出分类的结果,内容很简单,其实就是两个单词。但这里每行的内容其实都映射到前面原始数据里的file list里的分类。

接下来就可以使用这个命令了。新建一个test.bat文件,输入如下命令:

这里,小数为预测结果为dog或cat的概率。

用这种方法就可以对测试集中的数据进行测试了。

至此,对自己的图片的训练,以及测试验证就全部完成了。

故障排除

  1. 第一次运行命令进行训练后,程序hang住,从log看应该是消耗的内存太大了在模型运行之初可以在这个地方找到需要的memory信息:

    解决办法:将网络文件中的train和test的batch_size减小。

  2. 最开始训练的时候,发现每次验证的accuracy一直是0,导致训练无法继续,查阅资料后才发现是最开始生成file list中的label步是以0开头,而实际要求label必须以0开头,不可以随便乱取label的值。修改后问题得以解决

  3. 分类层的 num_output 设置问题

    网络中最后一层的num_output的数字,其值必须是我们设置的类别总数,否则训练会不收敛。在猫狗大战这个例子里,这个值必须是2。像 MNIST手写数字分类问题,由于最后输出是10个类别,所以这个值就必须是10

  4. 其他一些错误基本是粗心导致,如相对路径,引用参数使用错误,只要观察log,细心谨慎,即可避免错误。

关于作者 

钱彩红是英特尔软件与服务事业部的一名应用软件工程师,专注于在英特尔平台上与开发者的合作和业务拓展。力求将英特尔卓越的软硬件平台与合作开发者的软硬件产品完美结合,提供最优客户体验。


支持 OpenCV 的模拟仪表阅读器(Python* 环境中)

$
0
0

本示例应用截取模拟仪表的图像或帧,并使用计算机视觉读取数值。它包含两个部分:校准和测量。在校准过程中,用户向应用提供待校准的仪表图像,并提示用户输入数值范围(度)。然后在测量阶段使用这些校准值将刻度盘的角度转化为实用的数值。

>

您将学到的内容

  • 环形检测
  • 直线检测

收集资料

  • Python* 2.7 或更高版本
  • OpenCV 版本 3.3.0 或更高版本
  • 仪表图片(也可以使用提供的示例)

GitHub* 中本文待续

个人助理的能力

$
0
0

英特尔和亚马逊为未来智能家庭提供语音

我们的家庭日益智能化

我们的生活空间及其支持技术正日益智能化,不断丰富着我们的日常生活,帮助管理家务,令我们的生活高枕无忧。

请坐,音量调大

听、说、对话等语音控制技术正遍布家庭各个角落。

  • 电视、扬声器、闹钟、喷水器系统等技术将支持语音功能。
  • 设备将具备响应、感知和自动化能力。

个人助理的能力

英特尔® 语音支持开发人员套件是一款完备的音频前端解决方案,适用于远场语音控制,可加快产品设计进度,同时支持制造商集成 8-mic 环形阵列和 Amazon* Alexa* 语音服务。

产品开发人员可借助远场语音功能、语音识别、声学和低功耗要求,为各种类型的设备添加语音。

英特尔® 语音支持开发人员套件

英特尔和亚马逊通力合作,帮助开发人员更轻松地借助 Alexa 语音服务添加智能远场语音功能。

如欲获取关于该套件的完整概述,请参阅本文随附的 PDF 。 

订购套件,开启创新

更多关于英特尔® 语音支持开发人员套件的信息

IoT 101 系列第 3 部分:IoT 设备设计的实用性与自解释性

$
0
0

如果希望产品能够发挥全部潜能,那么实用性设计将成为整个设计流程的关键部分,因此在设计过程中必须考虑所有实用性原则,以便产品适应拥有不同技能的用户。

在某些情况下,良好的实用性设计是实现产品可靠性的基本要求。在设计阶段,设计者未考虑用户在其环境中的真实能力,是导致用户出错的主要原因。

不考虑实用性会妨碍用户发挥产品的某些特性,导致用户犯错并因此认为该产品毫无用处。

制定会对用户产生重要影响的设计决策时,设计者应充分考虑未来该产品的使用者在身体和心理两方面所具备的能力。

人类拥有短时记忆,能够瞬间记住约 7 个物体,因此如果同时向用户展示过多的信息,他们可能无法理解和/或管理所有信息。

人人都会犯错,尤其是需要处理过多信息或用户面临巨大压力的情况下;当系统发生故障并发送通知或触发告警,会进一步增加用户的压力,从而加大他们进一步犯错的几率。

人的身体能力各不相同,有的人拥有更强的观感,有的人是色盲,有的人擅长手工作业等等。因此不能根据自己的能力进行设计,而且不能假定所有用户都了解如何使用该产品。

这些人性因素是遵循以下设计原则的基础:

  • 用户熟悉度:产品应考虑该产品使用者的体验。
  • 一致性:功能设计应保持一致,以便以类似的方法,或以其他类似产品的方法来激活产品功能。
  • 最小惊讶:用户不应对产品的行为感到惊讶。
  • 可恢复性:产品应包含支持用户在出现错误的情况下恢复状态的机制。
  • 用户指南:产品应在出现错误的情况下为用户提供重要反馈,并根据环境提供帮助特性。
  • 用户多样性:产品应提供适用于不同类型的系统用户的交互功能

自解释性

这一术语由美国心理学家 James Gibson 于 1979 年在其著作《视觉直觉生态法》中提出。

每个物体都有自解释性,以及表面、事件和位置,自解释性越高,设备或工具使用的自动性和直观性越高。

例如,把手的出现应能最充分且自动说明如何开门:拉、推或滑动(自动向走廊两侧开启的门其自解释性较差,因为其开关原理不太直观)。

因此,我们借助自解释性来定义一个重要的设计参数:物体向用户建议相应使用行为的实体品质。

自解释性较好的物体有比如叉子或汤勺,以及几千年来经过不断改进而变得极其直观且使用起来非常简单的工具。

Previous: IoT 101 系列第 2 部分:IoT 设备的技术和设计

解决 UP Squared* Grove 物联网开发套件和 Arduino Create* 的连接问题

$
0
0

如果您发现采用默认方法无法连接 UP Squared* 主板和 Arduino Create*,可以尝试另外一种方法:使用串行终端连接主板。请执行以下步骤。

开始前须知

确保主板已连接至电源和以太网,如下所示。

board connected to power and Ethernet

另外确保 micro USB 线缆已插入 UP Squared 主板:

micro USB connected

您的主板应如下所示:

power and ethernet connected to board

连接至 Arduino Create

  1. 在计算机上安装串行终端应用(如果没有),比如 PuTTY*:http://www.putty.org
  2. 在主机计算机上打开“Device Manager”,并查找 UP Squared 主板条目,该条目将显示为“USB Serial Device”。请注意,主板的 COM 端口显示为(例如 COM4):

    device manager showing USB connection
  3. 在 PuTTY 中,将“Speed”设为 115200,并将“Serial line”设为前面找到的 COM 端口,(例如 COM4):
    screenshot of PUTTY session
    此操作将打开一个串行窗口,您可以用它来向主板输入命令。
  4. upsquared用作用户名和密码,登录主板。
    serial window used to enter commands

配置 UP Squared 主板

  1. 在主机计算机上,前往https://create.arduino.cc/getting-started/intel-platforms。用 Arduino 帐户登录。
  2. 给出选项后,点击“I have already installed an OS on my device”。

    screenshot of get ready tab selected

  3. 在下一个屏幕中点击My Device is Ready

    screenshot of select my device is ready

    注:如果没有 Arduino Create 帐户,请按照屏幕上的说明注册一个帐户。需要激活帐户并使用新帐户登录至网站。

  4. 选择 B:I HAVE A SCREEN AND KEYBOARD AVAILABLE.

    在终端窗口中,逐一输入 Arduino Create 提供的命令。此操作将在您的目标平台上安装一个 Arduino 接口。

    注:对于将运行的选项 B,您的主机和设备不需要在同一个网络上。 

  5. 运行命令完成后,返回至 Arduino Create 界面并点击“Next”以连接至主板。

    screenshot of options to select

为主板命名

  1. 为主板命名,如 up2 

    name your board

注:您的主板通过互联网连接至 Arduino Create 开发环境。如果断开主板连接并将其移至其他位置,应重新连接至 Arduino Create 环境。如果您使用的是代理设置,如果将主板移至没有代理的网络,需要重新进行设置。

后续步骤

现在主板已连接,可返回运行主板上的第一个项目

无缝边缘到云物联网集成助力加快上市速度

$
0
0

要点综述

依赖物联网 (IoT) 完成关键业务流程的企业努力整合数据孤岛、降低安全风险,和消除重复的基础设施。全面集成的边缘到云物联网基础设施解决方案可帮助改善业务洞察,从而提供真正的竞争优势。但这种解决方案的实施非常复杂,因此企业必须采用规划周密的方法,以顺利完成迁移。

英特尔和谷歌合作推出了一种基于标准的方法,可帮助物联网开发人员、OEM、独立软件厂商 (ISV) 和系统集成商 (SI) 开发无缝解决方案。借助基于英特尔® 物联网(英特尔® IoT 平台)和谷歌云平台* (GCP*) 构建的联合参考架构,物联网提供商可获得以下功能和优势:

  • 无缝数据获取。借助基于标准的参考架构,能够更轻松地收集数据和控制设备。
  • 端到端安全性。该架构经过精心设计,可有效保护设备硬件。
  • 轻松集成新设备。新设备可自动供应至平台,从而确保安全。
  • 强大的可扩展性。借助英特尔和谷歌的技术,企业能够按照需求快速扩展。
  • 更深入的洞察。GCP 的分析基础设施采用英特尔的边缘分析功能,可提供更深入的洞察支持快速制定决策,以及提供新服务和解决方案的机会。

英特尔® 物联网平台和 GCP 联合参考架构提供一种全面的方法,将设备层连接至网络层和云。

图 1.英特尔与谷歌打造的联合参考架构支持更轻松地
通过边缘到云连接物联网 (IoT),同时注重各层的安全性。

简介

物联网 (IoT) 可加快收集互联设备和传感器的数据,导致新设备和传感器的数量呈爆炸式增长,进而生成海量数据。这些数据可帮助企业制定更明智的决策,并以更快的速度推出新产品和新服务。Gartner Research 估计,到 2020 年,全球 250 亿台企业所有的联网设备将带来高达 2 万亿美元的经济效益。1这将为物联网解决方案提供商带来巨大的商机,但边缘到云解决方案的开发非常复杂。

物联网实施的技术挑战通常来源于企业中用于不同用例的多种物联网解决方案。这些用例包括在制造流程中监控化学品数量、办公室基于空间占用的照明,以及零售安全摄像头或监控可用停车位。多种实施可能会造成不同制造商的设备之间缺乏互操作性。成功的企业物联网解决方案需要从边缘到云,深入了解基础设施、安全性、集成和互操作性特征。尽管物联网实施比较复杂,但企业和解决方案提供商能够借助英特尔和谷歌的集成物联网解决方案,消除复杂性,满足不断增加的物联网需求。

解决方案架构

英特尔® 物联网(英特尔® IoT 平台)和谷歌云平台* (GCP*) 各自提供的功能和优势可帮助物联网开发商、OEM、独立软件厂商 (ISV) 和系统集成商 (SI) 开发行业标准的无缝解决方案。

解决方案概述和优势

英特尔® 物联网平台和 GCP 联合参考架构可将传感器、制动器和其他端点设备的数据无缝传输至谷歌* 云。明确定义的标准参考架构详细介绍了边缘、网络和云组件,可提供以下优势:

  • 无缝数据获取和设备控制,以提高互操作性。
  • 有效确保端到端数据和设备安全。
  • 自动载入以轻松部署启用安全性的设备。
  • 借助基于云的基础设施实现强大的可扩展性。
  • 通过 GCP 的分析基础设施获取客户洞察。
  • 借助其他服务和应用利用数据创造收益。

联合参考架构主要介绍:

  • 英特尔®物联网平台。主要阐明边缘组件、硬件安全、处理器,以及设备的供应、监控和控制。
  • 谷歌云平台 (GCP)。主要阐明各种云服务,包括数据获取、数据流、存储和分析。

联合参考架构介绍完之后是实施概述,以及附录 A 中所列举的物流和资产管理用例:物流和资产管理用例。

英特尔® 物联网(英特尔® IoT 平台)

英特尔® 物联网平台(图 2)包含一系列英特尔® 产品。该生态系统为支持轻松连接设备并将可信数据传输至云环境提供了坚实的基础。优势包括:

  • 多种系列设备。英特尔原始设备制造商 (ODM) 生态系统提供多种采用英特尔® 技术的设备和传感器。
  • 注重安全的解决方案。英特尔技术经过精心设计,可提高各个层面的安全性,并具备无缝设备预配置功能。
  • 增强的注册和管理。借助 Wind River Helix 设备云,可在云环境中从一个中心点控制设备管理和更新。

图 2.英特尔® 物联网平台可使用注重安全的软硬件解决方案,将各种设备连接至云。

谷歌云平台* (GCP*)

GCP 通过谷歌遍布全球的数据中心支持在云中托管启用安全性、经济高效、高性能的基础设施(图 3)。托管的服务支持访问该基础设施以获得整体解决方案。优势包括:

  • 完全托管的服务。谷歌管理整个私有基础设施的设置与维护,以便客户专注于构建解决方案。
  • 集成的开发体验。 GCP 提供各种服务,打造集成的端到端开发人员体验。
  • 完全控制环境。从数据获取到演示,开发人员可通过多种语言版本的 API 完全控制他们的计算环境。
  • 较大的规模和范围。 GCP 提供卓越的规模和范围,打造独特定位的计算和数据平台以解决各种物联网挑战。

图 3.谷歌云平台* 支持开发人员完全控制环境,无需设置和管理基础设施。

解决方案架构详细信息

英特尔® 物联网和 GCP 联合参考架构(图 4)主要利用三种组件和解决方案:英特尔® 边缘组件,比如硬件安全性和处理器;英特尔® 设备和安全管理,比如设备供应、监控和控制;以及 GCP 云服务,比如数据获取、数据流、存储和分析。

英特尔® 物联网平台组件

边缘组件

  • Wind River Linux*。借助内置认证的安全功能和便携性,Wind River* 为硬件提供物联网嵌入式 Linux 平台。
  • 英特尔® 安全基本要素。硬件信任根、功能(比如安全启动、可信执行环境 (TEE))和英特尔® Enhanced Privacy Identifier(英特尔® EPID)为硬件层的平台提供卓越的安全性。
  • 英特尔® 处理器。英特尔® Quark™ 片上系统 (SoC) 和英特尔® 凌动™、英特尔® 酷睿™和英特尔® 至强™ 处理器家族提供卓越的性能与可扩展性。

设备和安全管理

  • Wind River Helix 设备云*。 Helix Device Cloud 是物联网服务和技术产品组合,可帮助加快产品上市速度;它可提供设备监控、控制、软件更新、注册、验证和启用安全性的规模部署等功能。
  • 英特尔® Secure Device Onboarding。使用英特尔® EPID 的隐私保护特性,以及物联网身份标准、载入协议和对接服务 (rendezvous service),所有者启动设备时将自动完成设备注册。

GCP* 组件

GCP 组件可能因实施的不同而有所差别,主要分为五种功能:

数据获取

  • Cloud IoT Core*。 Cloud IoT Core 是一项完全托管的服务,支持轻松、安全地连接、管理和获取遍布全球数百万台设备的数据。Cloud IoT Core 结合谷歌云平台中的其他服务,可提供完备的解决方案来实时收集、处理、分析和显示物联网数据,以提高运营效率。
  • Cloud Pub/Sub*。 Cloud Pub/Sub 提供完全托管的实时消息服务,支持开发人员在独立应用之间收发信息。
  • Cloud Stackdriver Monitoring*。Cloud Monitoring 支持查看云应用的性能、正常运行时间和整体健康状态。Cloud Stackdriver Logging*。 Cloud Logging 支持开发人员保存、搜索、分析和监控日志数据和事件,并发送告警。

管道

  • Cloud Dataflow*。Cloud Dataflow 是一种统一编程模型,支持托管服务开发和执行多种数据处理模式,包括提取、转换、加载,以及批处理和连续运算。Cloud Dataflow 使开发人员免于处理各种操作任务,比如资源管理和性能优化。

存储

  • Cloud Storage*。GCP 提供一款对象存储解决方案,以实现卓越的物联网性能和价格优势。
  • Cloud Datastore*。Cloud Datastore 是一种 NoSQL 数据库,非常适用于移动设备和 Web 端点。
  • Cloud Bigtable*。 Cloud Bigtable 经过精心设计,适用于要求更高速度和更低延迟的工作负载,比如分析。

分析

  • Cloud Dataflow*。 Dataflow 提供编程基元,比如强大的开窗口和准确性控制,用于基于批处理和流处理的数据源。
  • BigQuery*。BigQuery 是用于分析的完全托管、PB 级规模、低成本的企业数据仓库。
  • Cloud Dataproc*。对于 Apache Spark* 和 Apache Hadoop*,Cloud Dataproc 经过精心设计,支持开源数据工具进行批量处理、查询、流传输和机器学习。
  • Cloud Datalab*。 Cloud Datalab 是一种交互式工具,只需点击一下便可发掘、分析和显示数据。

应用和演示

  • App Engine*。 App Engine 是一种平台即服务 (PaaS) 解决方案,用于开发应用,无需担心底层基础设施。
  • Container Engine*. Container Engine 是一种托管 Kubernetes* 解决方案,可提供特定于行业的解决方案,比如车队管理。
  • Compute Engine*。 Compute Engine 是一种基础设施即服务 (IaaS) 产品,可在各种客户操作系统上提供虚拟机。

图 4.英特尔® 物联网平台和 GCP* 联合参考架构详细说明了无缝设备载入的连接以及所有权隐私。

实施概述

请按照以下步骤连接设备、集成数据和管理软件升级(图 4):

载入设备

1.在制造过程中,芯片提供商将英特尔® EPID 证书嵌入至处理器的 TEE 之中。ODM 使用英特尔的开源工具套件创建全球唯一标识符,为英特尔® Secure Device Onboard(英特尔® SDO)服务分配 URL。英特尔® SDO 服务是一种自动化载入服务,设备通过该服务获取新的所有者信息。然后生成所有权代理,支持 GCP 以加密的方式验证设备的所有权。

2.购买时,与购买发票一起生成设备所有权代理。所有者将所有权代理导入 GCP,然后向英特尔® SDO 发送信号。

3.设备首次启动时,它联系英特尔® SDO,重新定向至新指定的 GCP 所有者提供的 IP 地址。

4.GCP 信任代理人和网关通过英特尔® EPID 签名和所有权代理验证该设备,然后注册该设备,以通过 GCP 和 Wind River Helix 设备云管理设备。

5.Wind River Helix Device Cloud 分配 GCP 提供的设备许可证,并配置网关的 pub/sub 主题订阅。

6.网关上的 GCP 物联网软件开发套件 (SDK) 使用设备许可证验证 GCP,并建立前往 GCP 的数据路径。

收集和集成数据

7.网关上的业务应用通过多种支持的协议(比如 Z-Wave*、ZigBee* 和蓝牙® 技术)获取互联传感器的数据。

8.网关上的 GCP IoT SDK 通过 MQTT 和 HTTP 消息协议将传感器数据传输至 GCP。

9.数据消息可进行路由、处理、存储和用于企业集成。

管理设备和软件更新

10.应用软件管理器使用 API 将更新推送至 Wind River Helix 设备云。

11. Wind River Helix 设备云准备已签名 RPM 软件包并将它们推送至网关。

12.英特尔® 物联网平台在网关上的管理代理负责更新软件。

总结

英特尔和谷歌的物联网端到端联合参考架构提供一款稳定、安全、简单的解决方案,它可提供各种工具和服务支持物联网开发人员创建高性能解决方案。借助支持的安全性和可扩展互操作性,英特尔® 物联网平台和 GCP 联合参考架构可提供构建模块支持任何行业的物联网应用开发。

该联合参考架构可重复使用、预配置和预验证。它可安全连接设备,并借助可互操作的软硬件将可信数据从边缘传输至云。每一层的设计重点在于安全性和采用英特尔技术的可扩展硬件,并面向性能优化,以支持各种工作负载。

寻求适合贵公司的解决方案。请联系您的英特尔代表或者访问 intel.com/securedeviceonboard

附录 A:物流和资产管理用例

随时查看货物的具体位置是供应链企业遇到的重大难题。市场研究显示,每年运输过程中因货物被盗损失约为 600 亿美元。2另外,全球生产出来供人类消费的食物有近三分之一丢失或浪费。3实时跟踪包裹(比如高价值或贵重物品)行程的能力就是公司如何通过物流管理、跟踪、报告和确保产品安全。(如图 A1 所示)。表 A1 展示了采用英特尔® 物联网平台和谷歌云平台* (GCP*) 联合参考架构的物联网解决方案。

图 A1.英特尔® 物联网平台和 GCP* 联合参考架构支持查看运输过程中货物的位置,帮助运输企业降低货物丢失的成本。

 

表 A1.物联网运输可见性用例的技术组件

组件

描述

p>智能传感器

 

运输通信信息中使用的多个智能传感器(使用电池)(温度、适度、震动、倾斜、掉落、压力、照明、接近),使用 IEEE 802.15.4 无线电连接至物联网网关。

采用英特尔® 物联网网关技术的物联网网关

运行 Wind River Linux* 操作系统的固定或移动网关(使用电池)放置在船运集装箱、卡车或货盘上

Wind River Helix 设备云*

基于 SaaS 的设备管理软件远程管理固定和移动物联网网关。

英特尔® Secure Device Onboarding

基于云的预配置软件安全载入固定和移动物联网网关。

谷歌云平台*

云 IaaS 和 PaaS 组件(例如 Cloud Pub/Sub*、Cloud Dataflow*、Cloud Storage*、Firebase* 和 App Engine*)使用 Pub/Sub 消息协议通过物联网网关获取、处理和分析智能传感器的数据。

 

1   gartner.com/smarterwithgartner/the-internet-of-things-and-the-enterprise

2   aic.gov.au/media_library/publications/tandi_pdf/tandi214.pdf

3   fao.org/save-food/resources/keyfindings/en 

     此处提供的信息可随时改变而毋需通知。请联系您的英特尔代表,了解最新的英特尔产品规格和路线图。

     描述的成本降低方案旨在用作示例,说明指定的英特尔架构产品在特定环境和配置下可能如何影响未来的成本和节省成本。环境将有所不同。英特尔不保证任何成本或成本降低。

    

     英特尔技术的特性和优势取决于系统配置,并需要借助硬件、软件或服务来实现。实际性能会因您使用的具体系统配置的不同而有所差异。没有计算机系统是绝对安全的。请联系您的系统制造商或零售商,或访问 intel.cn,了解更多信息。

     本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。

     蓝牙是其所有人的商标,授权英特尔公司使用。

     © 英特尔公司版权所有。所有权保留。英特尔、英特尔标识、英特尔凌动、酷睿、Quark 和至强是英特尔公司在美国和/或其他国家的商标。

*  Other names and brands may be claimed as the property of others.              1117/JBOS/KC/PDF             Please Recycle                        334992-002US

     

教程:通过推理加快计算机视觉应用的运行速度

$
0
0

Introduction

本教程将为大家详细介绍如何使用深度学习部署套件的推理引擎(包含在英特尔® 计算机视觉 SDK Beta 测试版 R3中)。推理指使用训练的神经网络从数据(比如图像)推理出某种意义的过程。在下面的代码示例中,视频(逐帧)馈送至推理引擎(我们训练的神经网络),然后输出结果(图像分类)。推理过程通过不同的神经网络架构(AlexNet*、GoogleNet* 等)来完成。本示例在 GoogleNet 模型上使用 Single Shot MultiBox Detector (SSD)。关于如何使用 SSD 的示例,请参阅英特尔® 开发人员专区的本文

推理引擎要求将该模型转化成 IR(中间代码)文件。本教程将详细介绍如何使用 Model Optimizer 提取现有模型 (GoogleNet) 并将其转化成 IR(中间代码)文件。

本教程结束后,大家将看到视频上通过检测多个对象(比如人或汽车)进行推理;例如下图,大家可以看到示例图像正进行以下操作:

在推理引擎上运行神经网络有何不同之处?

  • 推理引擎可优化推理过程,支持用户显著提高深度学习部署在英特尔® 架构上的运行速度。更多关于英特尔®处理器显卡的性能信息,请参阅本文
  • 推理可在硬件(比如英特尔® GPU 或英特尔® FPGA 加速卡)上运行,而非 CPU。

推理引擎如何运行?

推理引擎提取神经网络模型的表示并对其进行优化,以在 CPU 中充分利用高级英特尔®指令集,并使其兼容其他硬件加速器(GPU 和 FPGA)。为此,将模型文件(.caffemodel,.prototxt)提供给 Model Optimizer,然后该程序处理这些文件并输出两个新文件:.bin 和 .xml。运行应用时使用这两个新文件,而不是原始的模型文件。在本示例中,提供有 .bin 和 .xml 文件。

上图中的 IR(中间代码)指输入至推理引擎的 .xml 和 .bin 文件。

您将学到的内容

  • 如何安装 OpenCL™ Runtime 软件包
  • 如何安装英特尔® 计算机视觉 SDK Beta 测试版 R3
  • 如何从 Caffe 模型生成推理引擎所需的 .bin 和 .xml(IR 文件)
  • 使用生成的 IR 文件在 C++ 应用中运行推理引擎
  • 比较 CPU 和 GPU 的性能

收集资料

  • 第五代英特尔® 酷睿™ 处理器或更高版本。可以运行 ‘lscpu’ 命令在 Linux* 中查找产品名称。‘Model name:’ 包含处理器的相关信息。

:产品名称中包含有代际编号,在 ‘i3’、‘i5’ 或 ‘i7’ 之后。例如,英特尔® 酷睿™ i5-5200U 处理器和英特尔® 酷睿™ i5-5675R 处理器都是第五代产品,而英特尔® 酷睿™ i5-6600K 处理器和英特尔® 酷睿™ i5 6360U 处理器都是第六代产品。

  • Ubuntu* 16.04.3 LTS
  • 如果在生成的 GPU 上运行推理:
    • 采用英特尔® 锐炬® Pro 显卡或高清显卡
    • 不安装独立显卡(OpenCL™ 平台要求的)。如果有,请务必在安装之前,在 BIOS 中禁用该显卡。
    • 不安装用于其他 GPU 的驱动程序,或支持其他 GPU 的库

详见GitHub*

深入了解对象检测、识别和跟踪

$
0
0

Through machine learning, computer programs learn how to identify people and objects.

概述

下面我们首先通过查字典定义和区分对象检测、对象识别和对象跟踪这三个术语。然后探讨每个过程所涉及的一些算法,以加强我们对这些定义的理解。

计算机视觉三位一体

计算机视觉术语对象检测对象识别通常可以互换(应用的命名通常取决于编写程序的作者)。另一术语对象跟踪经常与检测和识别算法一起使用。这三个术语相互合作,帮助打造更加可靠的应用,尽管可能不清楚它们之间的区别和联系(跟踪是否只是检测的延伸?)。但我们可以首先参考标准字典,然后了解各个过程的相关算法,明确区分这些过程。

想想对象发现(而非检测)和对象理解(代替识别),可能会有所帮助。当然,我们知道理解一件事和发现那件事完全不同。除了语义之外,我们还希望了解各个过程所涉及的算法(以基本了解这些算法的设计宗旨)。当我们了解了应用特定类型的算法会带来哪些结果后,就可以通过字面意思了解这些术语之间的差异 — 关乎于过程和结果。但字面意思非常重要,因为它们首先会影响我们在头脑中反映现实的方式。因此我们首先从检测的标准定义开始。

检测

检测算法提出问题:那儿有东西吗?

发现行为

字典通常可以帮助我们明确了解词语字面所包含和不包含的意思(尤其是当编程人员和设计人员以不同的方式使用这些词语的时候)。《韦氏词典》*将检测定义为:

发现、看到或注意到某物的过程。

这里的“某物”可以是任何事物(一只鸟、一架飞机或一个气球)。重点是看到某物那里。

对象检测的目的是注意到或发现(图像或视频帧内)对象的存在。能够将对象(不同的像素子集)与静态背景(较大的像素集,一帧一帧大部分时候保持不变的事物)区分开。但我们如何从背景中锁定对象?图像和视频的处理方法不同。

图像检测和视频检测

对象边界(图像)

由于照片是静态图像,我们无法通过运动来检测照片中的对象,必须依靠其他方法解析出场景。我们呈现真实场景的照片(一条繁忙的市区街道包含许多不同、重叠的对象和表面),该场景的繁忙特性使我们难以对其进行解析(知道对象的边界)。边缘检测法(例如 Canny 边缘检测)可帮助确定此类场景中的对象。边缘指对象边界,可通过查看图像中的强度如何变化(图像灰度级的突然变化)来发现边缘。知道边缘的具体位置不仅有助于检测明显的对象(倚白墙放置的蓝色自行车),还有助于我们准确解析对象重叠的复杂场景(一个人坐在椅子上可能被视作两个不同的对象,而不是一个大的混合对象)。

下面是有关 Canny 边缘检测的示例。我们使用 OpenCV* 库的算法cv2.Canny()查找图像中的边缘。查找边缘(图 3)之前,首先将图 1 转化为灰度(图 2)。转化为灰色色图(灰度)能够增加图像的对比度,有助于更轻松地识别像素。

图 1.原始照片。

图 2.可增强图像对比度的灰度色图。

图 3.应用于灰度图像的 Canny 边缘检测。

帧(视频)中的像素有所不同

当物体出现在新的一帧,而前一帧没有(块上的新像素),我们可以设计出一种算法去发现并以检测的形式登记这种差别。为了注意到之前不在该处,但现在出现在该处的事物 — 这算是检测。而且与上述定义相符,视频检测技巧的示例包括背景减除法(常用于创建前景模板,比如 MOG(指高斯混合)和absdiff (绝对差)。

背景减除背后的理念

与静态图像不同,我们要在视频中处理多帧,这样我们可以实施背景减除法。背景减除背后的基本理念是生成前景模板(图 6)。

我们首先逐帧进行减除 — 当前帧(图 5)减前一帧(图 4)— 找出不同之处。

图 4.前一帧(背景模式)。

图 5.当前帧。

然后将阈值应用于不同之处,以创建一张包含场景中所有正在移动或新对象的二进制图像。这里的“不同之处”指在场景中飞行的无人机(检测到的对象)。

图 6.前景模板。

背景减除算法 MOG

高斯混合 (MOG) 不能与常见的方向梯度直方图 (HOG) 特性描述符混淆,HOG 技巧常与支持向量机(监督式机器学习模型)搭配使用,用于将对象分为“人类”或“非人类”。与执行分类任务的 HOG 不同,MOG 方法实施高斯混合模型以减去两帧之间的背景。借助检测技巧,此处关乎于(两帧之间的)不同之处。But 哪些不同之处(该对象是人?机器人?)还不是我们关心的地方。如果我们的目的是识别或分类某一对象,那么我们要用到的是识别技巧。

如何收到检测通知

为了提示我们(提供某种视觉提示)检测到对象,通常在检测到的物体周围画上矩形或方框(颜色鲜艳的那个)。如果(视频中)帧切换时发生变化,算法会喊出“嗨,刚刚在这一帧中出现(或移动)的一组像素是什么?”,然后决定“快!在周围画一个绿色方框,让人类知道我们检测到了物体。”

下图 7 显示了采用背景减除法的应用(使用实时流媒体播放)检测到对象。但该应用不提示该物体是什么。它仅查找前一帧没有出现的大范围像素 — 查找不同之处。

图 7.鬼鬼祟祟的 BunnyPeople™ 玩偶无法躲过基于背景减除法的应用的检测。

表 1.检测技巧和 OpenCV* 库的函数

检测技巧OpenCV 库的函数/类示例
背景减除法
边缘检测
Absdiff
cv::bgsegm::BackgroundSubtractorMOG
cv::bgsegm::BackgroundSubtractorGMG
Canny

从抽象到具体

气体检测器指检测或感知是否存在气体的设备。根据设备精度的不同,如果存在甲烷、酒精气体、丙烷和其他化合物,可发出警报。金属探测器指检测是否存在金属(对于金属探测器来说,金、铜和铸铁都是一样的)的工具。对象检测器可发现是否存在对象 — 对象所在区域是这一帧中的像素范围。从抽象到具体时 — 从气体到甲烷,从金属到黄金,从对象到人 — 暗含着我们之前对具体事物的了解。通过这一点就可区分检测和识别 — 知道该对象是什么。我们可以认出检测到的气体是甲烷。我们可以确定探测到的金属是黄金。我们可以认出检测到的对象是一个人。对象识别技巧可帮助我们创建更精确的计算机视觉应用,以对该对象的细节(人或猿猴、男性或女性、鸟或飞机)进行处理。识别就像是在检测的基础上放上一副验光眼镜。放上眼镜后,我们就可以识别出远处微小模糊的对象实际上是猫,而不是石头。

识别

这里,算法更加好奇,问道:那儿有什么

之前有所了解

《韦氏词典》*将识别定义为:

 

由于之前有所了解或经验而知道是谁或某物的行为。

根据这一定义,我们知道对象识别是识别或了解(图像或视频帧中)事物性质的过程。识别(应用)可以基于匹配、学习或模式识别算法,目的标记(分类)某一对象 — 从而提出问题:该对象是什么

下图来源于鸟类识别与标记应用(使用英特尔® Movidius™ 神经计算棒)。更多关于该示例应用的信息可访问GitHub*。请注意标记“秃鹰”后面的“1.00”。它是与识别有关的置信度,此处算法百分百确定该对象是一只秃鹰。但对象识别并不能总保持这种精确度。

图 8.完全肯定地识别秃鹰。

在另一张图像(图 9)中,相同的应用不能完全确定在海岸线上盘旋的对象。尽管不能识别左侧远处的对象具体是什么,但能够准备将其与通用的对象类别“鸟”联系起来。对于其他对象,该应用持观望态度 — 无法确定该对象是信天翁或似乎是仓鸮。

图 9.经过训练以识别鸟类的识别应用无法确定图像中的对象。

而且,由于术语的使用不一致,有些人可能认为(检测识别)是一回事。但经过参考上述定义,可以肯定的是对象检测(发现某物在那儿)不能等同于识别某物是什么(由于算法之前有所了解,所以能够准确认出该对象)。

识别行为(我知道那个对象是秃鹰,与检测行为(我看到那儿有东西)是不同的。但当看到对象时,算法如何准确得知那是秃鹰?我们能否告诉算法如何通过其他鸟类知道该对象是秃鹰?大家知道,编写一款详细介绍秃鹰和其他鸟类的习性的计算机程序。事实证明,有些事我们无法有效计算机(提供指令),因此我们必须设计能够自主学习的算法。

学习经验的算法

我们使用识别技巧(足够聪明以区分海鸥和飞机的算法)进一步探明对象的性质。有些算法能够准确分类对象,因为它们经过训练 — 我们称之为机器学习算法。而且算法获取关于某物(比如鸟类)知识的方式是通过训练数据 — 接触数万张不同鸟类的图像,这样该算法能够学习识别不同种类的鸟。机器学习算法之所以适用,是因为它们能够提取图像的视觉特征。然后通过这些特征将某张图像(第一次看到的未知图像)和其他图像(在之前训练过程中“看到过”的图像)联系起来。如果我们在前文引用的识别应用(图 8)没有经过标记为“秃鹰”的图像训练,那么它在看到该图像时无法将鸟标记为秃鹰。但它仍然足够聪明,知道它是一种鸟(如图 9 所示,左侧远处的鸟标记为“鸟”但不知道具体是什么)。

表 2.识别技巧和 OpenCV* 库的函数。

识别技巧OpenCV 库的函数/类示例
特性提取和机器学习模型
HOG 和支持向量机 (SVM)
深度学习模型(卷积神经网络)
FaceRecognizer
FaceRecognizer::train
createEigenFaceRecognizer
Fisherfaces for Gender Classification

跟踪

跟踪算法想知道某物在前方哪个位置。

密切关注某物

跟踪算法不能让某物消失。这些顽强的算法将一直跟随你(如果你是它们感兴趣的对象),无论你走到哪里都跟着你。至少这就是我们对于理想跟踪器的期望。

《韦氏词典》*将跟踪定义为:

 

跟随或观察某物或某人的移动路径。

对象跟踪的目的是关注某物(连续视频帧中对象的移动路径)。跟踪算法经常基于或搭配对象检测识别构建,旨在定位(和密切关注)视频流中某一移动的对象(或多个移动对象)。

下面是某一对象的定位记录(跟踪通常处理彼此相关的多个帧),帮助我们了解其位置随着时间的推移是如何变化的。这意味着我们有一个对象移动模型(提示:模型可用于预测)。卡尔曼滤波器(一系列数学方程式)可用于确定对象将来的位置。通过随时间推移所做的一系列测量,该算法支持对过去、现在和将来的状态进行预估。

当然,状态预估可用于跟踪,而且在移动对象情况下,我们希望预测其将来的状态 — 对象尚未做出的未来移动。但我们为何要这样做?对象可能会被遮挡,如果我们最终的目标是维持各帧中的对象身份,了解对象未来的位置可帮助我们处理遮挡情况(当事物成为障碍)。

遮挡问题

遮挡是妨碍大家密切关注某物的问题 — 即对象被暂时遮挡。我们一直在跟踪繁忙街道上的特定行人,后来他们被公交车挡住了。强大的跟踪算法能够处理这一临时遮挡情况,继续锁定感兴趣的人。事实上,这是比较难的一部分 — 确保算法锁定相同的事物,以确保不跟丢目标。即使行人已不在图像中(公交车像素将行人隐藏起来),该算法能够想到他们可能前往的未来路径。因此,我们能够继续有效地跟踪这一行人,尽管有大量障碍遮住了我们的视线。

表 3.跟踪技巧和 OpenCV* 库的函数。

跟踪技巧OpenCV 库的函数/类示例
卡尔曼滤波,CAMShiftcv::KalmanFilter Class
TrackerMIL
TrackerTLD
TrackerMedianFlow

各不相同但不互相排斥的过程

对象检测过程看到某物(我们称之为“对象”的像素子集)在那儿,对象识别技巧用于知道某物是什么 (将对象标记为具体事物,比如鸟),对象跟踪支持我们跟踪某一特定对象的移动路径。

通过准确定义,我们可以将这些过程视作各不相同,相互独立的过程。而将这些定义与其中涉及的算法结合起来有助于我们进一步了解这些术语是不能互换的 — 检测不是识别的近义词,跟踪也不仅仅只是检测的延伸。如果我们知道检测(基于真正的词意)的结果,我们就会知道,检测算法的目的不是分类或识别某物,而是单纯地注意到了它的存在。我们还知道,譬如卡尔曼滤波器等跟踪算法(能够确定对象未来的状态)不仅仅是譬如背景减除的延伸。识别关乎于对事物之前的了解,而检测不是。

现在我们知道,这些术语之间的区别不仅仅是词意的不同,而是过程和结果(基于它们的目的和所涉及的算法的结果)的不同。尽管它们之间各不相同,但这些计算机视觉过程之间没有好坏之分,它们经常相互合作,共同创建更加高级或强大(可靠)的应用 — 例如检测和识别算法搭配使用,或当跟踪出现故障时检测可用作备份。每个过程 — 对象检测、对象识别和对象跟踪 — 都有各自的目的,并相互补充,但......如果我们最终要搭配这些过程(想出一些特别聪明的算法组合)以创建实用、可靠的计算机视觉应用,我们首先需要知道如何区分它们。

代码示例

如需进一步了解本文中提供的部分算法和技巧,请查看 下方 GitHub* 中英特尔物联网开发人员套件库的代码示例。面部访问控制代码示例使用 OpenCV 库中的 FaceDetectorFaceRecognizer类,而运动热图则基于背景减除 (MOG)。


英特尔® 高清晰度音频规范更新

$
0
0

最近,英特尔® 高清晰度音频规范进行了多项变更。具体信息详见一份重要的白皮书,本页面提供了该白皮书的 PDF 版本。

功能变更

以下是英特尔® 高清晰度音频规范新特性的高级概述:

  • 使用功能链表简化新功能的宣传和发现。
  • 该规范目前支持多异构链路(第一个使用功能链表框架的特性)。
  • 新增对静态切换至较低频率的支持。该支持减少了对配置的损耗,只需固定的低带宽连接。
  • 支持一种新型的高清晰度音频编解码器,无需 RST# 引脚。

如欲获取更多信息,请下载附带的 PDF

更多信息

英特尔 3D Xpoint™ 技术产品 – 现有特性和即将推出的特性

$
0
0

概述

由英特尔和美光科技联合开发的 3D Xpoint™ 技术最初于 2015 年通过一篇名为 “英特尔与美光科技联手开发突破性内存技术”的新闻稿发布。您是否对 3D Xpoint 技术感兴趣,想要了解英特尔目前正在发布什么产品或计划未来发布什么产品?请继续阅读,了解英特尔在 2017 年 10 月之前发布的内存和存储产品、持久内存 DIMMS 以及预计 2018 年上市的产品。

英特尔® 傲腾™ 技术

英特尔® 傲腾™ 技术品牌合并了 3D Xpoint™ 内存介质、英特尔内存和存储控制器、英特尔互连 IP 和英特尔® 软件。这些构建模块提供出色的高吞吐量、低延迟、高服务质量和高耐用性,可轻松处理需要大容量和快速存储的消费者和数据中心工作负载。

如今,您可以买到两种基于英特尔傲腾技术的产品:

  • 英特尔® 傲腾™ 固态盘 DC P4800X 系列 – 面向数据中心
  • 英特尔® 傲腾™ 内存 – 提供优化的 PC 体验  

英特尔® 傲腾™ 固态盘 DC P4800X 系列

英特尔傲腾固态盘 DC P4800X 系列是第一款将内存和存储属性相结合的产品。这款产品提供一个新的存储层,打破了传统 NAND 存储的瓶颈,提高了应用的运行速度和每台服务器的工作效率。

”使用英特尔®

使用英特尔® 傲腾™ 固态盘扩展内存,获取更大的内存空间

您无需更改应用即可利用这一扩展内存池,因为该软件可主动管理 DRAM 和固态盘之间的数据传输,从而提供最佳的容量与性能组合。人工智能 (AI) 和数据库应用是这一技术的直接受益者。如欲了解更多产品信息,请阅读 英特尔 Memory Drive 技术产品简介

英特尔® 傲腾™ 固态盘 900P 系列

英特尔® 傲腾™ 固态盘 900P 面向高性能台式机和工作站,旨在提供高随机读写性能和超低延迟,可让专业用户、创作者和发烧友获得更高的平台性能。软件开发人员可以优化应用,从而充分利用英特尔® 傲腾™ 技术的独特特性:低延迟、在低队列深度下实现高吞吐量和高服务质量 (QoS)。如欲了解更多信息,请阅读英特尔® 傲腾™ 固态盘 900P 系列产品简介,查看英特尔傲腾固态盘 900P 系列产品页面。 

英特尔® 傲腾™ 内存

对于 PC 消费市场,英特尔傲腾内存的标准 M.2 外形尺寸模块中包含 3D Xpoint 内存介质。它在使用第七代智能英特尔® 酷睿™ 处理器平台构建的 PC 上插入标准 M.2 接头。英特尔傲腾内存在 DRAM 和 SATA 硬盘之间建立了一个纽带,并使用 英特尔® 快速存储技术(英特尔® RST)软件加速访问重要文件。

”英特尔的持久内存

英特尔的持久内存 DIMM

借助持久内存 DIMM,英特尔正在革新存储层级,在距离处理器很近的位置处理大量数据。这将支持新的使用模式并使 SAP HANA 等内存应用更加强大。作为英特尔® 至强® 处理器可扩展产品家族(代码:Cascade Lake)更新工作的一部分,持久内存 DIMM 将于 2018 年推出。软件开发人员现在可以开始准备用于持久内存的应用,以便为明年的产品发布计划做好准备。请访问英特尔开发人员专区 Persistent Memory Programming网站,获取更新软件所需的信息。如欲获得更多产品详情,请继续关注这一网站。

总结

您可以利用英特尔傲腾技术来满足数据中心或家用 PC 的需求。在数据中心内, 您可以使用英特尔傲腾固态盘 DC P4800X 系列来提升缓存和存储性能,或者使用英特尔 Memory Drive 技术扩展内存。在家里,英特尔傲腾内存可帮助您更快速地执行所有操作,如运行工程应用,畅玩游戏,创建数字内容和网页浏览等日常活动。

作为英特尔® 至强® 处理器可扩展产品家族(代码:Cascade Lake)更新工作的一部分,英特尔的持久内存 DIMM 计划于 2018 年上市。

关于作者

Debra Graham 从事过多年的企业存储应用开发工作。现在她经常在英特尔开发人员专区上发布文章和视频,以帮助开发人员更好地使用英特尔® 技术。

使用 Blender* 重新拓扑 VR 和游戏资产

$
0
0

retopolgized images

本文将介绍如何将网格重新拓扑成一个整洁的低密度模型,然后 UV 解包该网格,以便将纹理贴添加至新模型。本文还将探讨如何使用免费工具,比如 Blender* 及其 Bsurface 插件,重新拓扑雕塑的 3D 网格。

关于重新拓扑

重新拓扑指一种重新构建 3D 网格使其规模更小、设计更整洁、更便于使用的行为。重新拓扑看起来似乎只是 3D 创作人员的课间自习,但这一任务非常关键,且有利于灵活创作模型。大多数原始网格使用 3D 建模程序构建而成,经过了雕塑或扫描,可捕捉细节层以使模型变得美观。然而,同一细节会生成较大的文件,其读写操作和所需的内存对目标应用来说存在一定难度。而且大多数细节丰富的网格细节过多且不对称,生成网格动画时会产生大量奇怪或无用的折痕、折叠和移动。

high topology mesh and renderlow topology mesh and render

为避免出现这种情况,需要将网格重新拓扑成更基础、更实用的结构,然后将该 3D 结构的颜色、碰撞和折痕等详细数据传输至“合并”的2D 材质贴图。这样可减少 3D 网格顶点的数量,缩小文件规模,同时渲染效果保持不变。在上图中,原始网格的面数从 500,000 减至 2,000,但输出的外观相对一致。

开始前的准备工作:

  • PC:Mac* 或 Linux* 计算机;推荐使用英特尔® 酷睿™ i5 或英特尔® 酷睿™ i7 处理器
  • 启用 BSurface 插件的 Blender 3D(免费下载)

Blender* 和 BSurface

Blender 是一款免费、功能齐全的 3D 建模、渲染和动画工作室工具。借助这款强大的工具,你可以在 Blender 中创建简单的 3D 对象或生成全特性动画影片。为实现本文的目的,我们重点使用 Blender 的建模功能。如果之前用过 Blender,建议观看 入门视频学习如何设置 Blender 以获得比较理想的结果。

如需完成本教程中的任务,需要一个高拓扑 3D 网格。出于教学目的,我们使用 Blender 中的一个网格对象 — Suzanne 猴头模型。如需创建顶点数超过 500K 的高密度版模型,请执行以下步骤:

  1. AddMesh,然后选择 Suzanne
  2. Tools面板下方选择 Smooth
  3. Properties面板下方点击 wrench 图标以获取修饰符。
  4. 添加名为 Subdivision Surface的修饰符。
  5. Subdivision设为 5。
  6. 单击 Apply

使用 BSurface 重新拓扑

本视频介绍了以下重新拓扑步骤。

设置 BSurface

如需安装 BSurface,从 File菜单单击 User Preferences。选择 Add-Ons选项卡。搜索 “bsurf”。此时右侧将显示 BSurface 插件。启用该插件时,选中插件名称左侧的方框......此时 BSurface 应已安装并启用。

要想 BSurface 将右侧的顶点与原始模型对齐放置,必须在场景中的“对象模式”下加载和最初选择原始模型。然后创建重新拓扑、添加平面,这样就可以创建一个新的资产。前往 Edit 菜单,然后选择 Snap to Features

配置 BSurface 时,从 Tools菜单中选择 Grease Pencil选项卡。然后进行以下操作:

  • 选择继续。
  • Data Source下方选择 Object
  • Stroke Placement下方选择 Surface

surface button in the menu

你可能会发现,可以手动移动部分顶点、边缘或平面。要想这些条目同时贴住模型表面,请在编辑模式下选择以下选项:在 Snap下方选择 on,然后在 Snap Element下方选择 Face。最后,点击右下方的 4 个图标。

face button in the menu

现在您已准备好使用 BSurface。

创建第一个栅网

BSurface 可根据你在原始网格上绘制的一些线条自动创建网格。

  • 只需朝一个方向绘制几条平行线。BSurface 将根据这些线条创建一个交叉网格。不要绘制不同方向或交叉方向的线条。
  • Tools面板下方的 Tools选项卡中,可以看到 BSurface。单击 Add Surface按钮。

steps to add surface

  • 如果要调整创建的内容,可设置 Cross strokesFollow的数量(绘制的线条之间的线条)。此外,如果你想要 BSurface 忽略除第一条和最后一条之外的其他线条,可以清空 Loops on strokesFollow现在就是第一条和最后一条线之间的细分部分。

cross strokes example

创建径向网格

你通常需要一个围绕某个区域(比如眼睛或嘴巴)的网格。要创建这种类型的网格,只需从围绕的区域绘制均匀分布的射线。

radial mesh

单击 Add Surface,然后选择 Cycle Cross,以结束径向并设置所需的环形交叉的数量。

radial mesh example

和之前一样,可以清空 Loops on strokes以更均匀地设置细分。

角落填充

你会发现,通常网格两侧完成后,仍然需要填充两侧之间的网格。为此,可以选择两侧上的所有点。使用 SHIFT+ RIGHT+CLICK 取消选择角落的点,然后绘制缺失的一侧。在下列示例中,设置并选择顶部和右侧的边缘。按照网格点的弧线绘制底部边缘。单击 Add Surface进行填充。

corner fill

corner fill grid

连接网格

如需连接两个网格,请选择需要连接的所有点。请确保每侧所选择的点的数量相同。然后想象它们之间的一系列线条。绘制至少两条线,最后一条线在突显的顶点集上进行绘制。

connecting meshes

connecting meshes detail

现在你已经基本了解了快速重新拓扑模型所需的 BSurface。继续使用 BSurface 在高拓扑模型上创建完整的网格。

UV 解包和添加详细的纹理贴图

UV 解包是这一流程的关键组成部分,也是管道的一部分,可帮助你将高拓扑模型中丢失的细节上传到刚刚重新拓扑的模型上。

UV 解包是定义如何将 2D 图像映射在网格的所有顶点上的过程。可以想象一下地球模型,你有一个光滑的球体,并计划生成有关地球上包裹的所有特征的打印图像。对于包裹住地球的 2D 图像,你需要合理地裁剪图像,使其包裹住球体。这称之为地图投影。

texture map
图片来源维基百科:古德等面积投影的世界

对于 3D 建模,UV 解包模型支持你定义这种投影模式,从而了解如何将所有类型的 2D 信息映射到 3D 对象上。在地球示例中,你可以将信息合并成包含有关地球陆地或水的色彩、粗糙度/光泽度和高度信息的 2D 图像映射,然后使用 UV 贴图把它投影到球体上。

以下示例展示了如何 UV 映射 3D 外星人头部模型。左侧的图像称为色彩贴图,中间的图像称为法线贴图或凹凸贴图。两张图像都有在各自图像上进行扁平化处理的线框网格。这种扁平化网格模式就是 UV 贴图。

UV map

所需内容

在这部分教程中,你需要一个包含纹理细节的高多边形网格,可以是能用的文件,也可以是创建并用 Sculpt 添加纹理细节的高多边形对象。链接中的文件是雕刻了皱纹、线条和皮皱的猴子头部。

首先确保高多边形网格和重新拓扑或低多边形网格在同一空间中。使用属性面板中的转化选项将 X、Y 和 Z 轴设在同一位置。

本视频还介绍了需要执行的步骤。

第一步:标记接缝处

标记接缝处是一种选择过程,将网格裁剪或分解以使其成为扁平 2D 模式的 3D 模型。例如,衬衫或毛绒玩具等将 2D 材料缝在一起所形成的接缝处。而且你肯定不希望接缝处太多,也不希望接缝位于显眼的区域。将它们放在有大量曲面几何体以及使自然边缘看起来合理的地方。如果像我们一样合并纹理贴图,接缝处可能不会显示出来。但如果使用噪音或其他模式的程序化贴图,这些模式通常在接缝处断裂。

如果要标记接缝处,请选择“低多边形对象”,然后前往编辑模式。选择 Edge 选择工具,然后选择想要放置接缝处的位置。

marking seams example

  • 选择 Mesh Menu> Edge> Mark Seams。每一个想要标记的接缝处都要这样操作。

steps to follow

模型上的接缝显示为红色边缘。

red colored edges of seams

第二步:解包低多边形网格

这一步非常简单,包括解包模型,然后创建待映射的纹理文件。需要再次进入低多边形模型的编辑模式。选择所有选项,验证每个网格面是否都已选择。输入 “U”,以显示 Unwrap 菜单。选择菜单顶部的 UV Unwrap

对于待运行的 UV 贴图,你希望它能够为即将合并或渲染的纹理文件提供指南。创建新的视口窗口,将该视口设为 UV Image Editor。在视口中的 Image下方选择 New Image

red colored edges of seams

设置所需的分辨率选项,命名,然后保存。

steps to unwrap the low poly mesh

现在它将变成一张不包含任何内容的黑色图像。但是你可以从黑色图像上看到 UV 贴图。

steps to unwrap the low poly mesh

第三步:合并高多边形网格的法线贴图

在这一步,将高分辨率模型的纹理信息“合并”成一个称为法线贴图的 2D 图像文件。然后将这种纹理添加到低分辨率模型的材料上。第一步是确保你的低分辨率模型有包含纹理的材料。

选择低分辨率或重新拓扑模型。在 Properties面板中前往 Materials选项卡。如果没有材料,添加材料并选择 Nodes。(请确保 Blender 应用顶部的渲染设置设为 Cycles Render。)

cycles render

将其中一个视口设为 Node Editor,以打开 Node Editor。从菜单Add菜单选择 Texture,然后选择 Image Texture

steps to select image texture

添加完这些后,将文件设为你之前创建的 UV 图像的名称。同样将其设为 Non-Color data

steps to select non-color data

接下来需要选择合并设置。在 the Properties Panel下方选择 Render设置,它为摄像头图标。滚动到 Bake部分的底部并执行以下操作:

  • 对于 Bake Type,选择 Normal
  • 对于 Space,选择 Tangent
  • 选择 Selected To Active
  • Ray Distance设为至少 1.00。

bake section

接下来的部分可能难度较大。你需要选择两种模式,同时需要确保首先选择高分辨率版本,然后选择低分辨率版本。务必处于对象模式,而非编辑模式。

如果处于对象模式,使用 Asset Inventory面板验证高拓扑模型和低拓扑模型是否可见,而且名称右侧是否有眼睛图标。选择高拓扑模型,使其显示为白色。右击,然后点击 Select。应看到用金色部分选择的模型,名称出现在 3D 视口中。

bake section

现在按住 SHIFT 键,然后右击 3D 视口中的低多边形模型。这样操作后,低多边形模型的名称将显示为白色,3D 视口中出现低多边形模型的名称,而且选择颜色将从金色变成橘色。完成后,在 Render面板中单击 Bake

steps to select high topology

第四步:将法线贴图应用于模型

在这最后一步中,我们提取合并文件,将其连接至低多边形模型的材料。首先需要保存上一步的 Baked 渲染器。在视口中 UV 图像贴图的下方,首先验证保存的 UV 贴图的名称是否显示在文件浏览器中。图像应涂成蓝色/紫色,与下图类似。如果是编辑模式,左侧的图像显示的是法线贴图;如果是对象模式,右侧的图像显示的是合并图像。在 UV 贴图图像编辑器中,选择 Image Menu,然后单击 Save

texture ready to connect to the model

现在我们准备连接纹理和模型。

前往包含“纹理图像节点”的“节点编辑器”。将“纹理节点”连接至“法线贴图节点”。在 Add> Vector> Normal Map下方获取“法线贴图节点”。

image in normal map mode

将法线贴图模式连接至 Principled BSDF Shader 的法线插槽。现在,你应该能在低分辨率模型中看到高分辨率模型的所有细节。

image in normal map mode

现在你已完成用于游戏或 VR 软件的 3D 资产工作流的主要部分。除了模型的纹理法线信息外,还可以用其他创建真实皮肤、头发或其他材料(包括开裂的混凝土和带有刮痕的炮铜)的材料贴图制造纹理。如欲参考接下来的工作流,请使用 Substance Painter*和 Blender 等工具搜索 PBR 材料。

实例详解神经网络的back propagation过程

$
0
0

前言

对于神经网络的初学者而言,神经网络的前向传播(Feed Forward)应该是比较易懂的。而反向传播(Backward Propagation)可能是第一个“烧脑”的点。很多神经网络书籍或技术博客里都对其原理有过介绍,但较少有文章会通过简单的实例一步一步的来介绍反向传播是如何计算出来的。有些把自己定位为数据科学家(data scientist)的人会认为这么底层的细节不需要懂,但实际不然,在开发新的神经网络时往往会需要引入一些新的神经网络层,这个时候就需要知道如何编写反向传播的代码。在这里我会通过一个简单的例子对整个反向传播过程进行推导,希望对神经网络感兴趣的初学者有所帮助。最后我会基于Caffe的pycaffe接口来介绍如何使用pycaffe来验证手动计算的结果。

PS:为啥用的是Caffe而不是Pytorch或TensorFlow? 原因很简单,我是Intel Caffe开发团队的。

PS:读这篇文章的同学需要有基本的数学知识,比如标量/向量微积分和链式法则(这里推荐一篇很好的论文“深度学习中你所需要了解的矩阵微积分”)。另外需要对Caffe有一定的了解,因为我会用Caffe来对整个推演过程做验证。

正文

为演示反向传播的整个过程,我们使用AlexNet神经网络拓扑结构来做实例讲解,为了计算方便,我们剪裁了绝大多数层,仅保留最后面几层:

[数据层] -> [全连接层fc7 + ReLU激活函数] -> [全连接层fc8] -> [Softmax with Loss层]

在Caffe中该神经网络拓扑表示为如下结构,其中方块表示神经网络层,多边形表示数据输入输出块(每个输出块都是下一层的输入块),圆形表示该次前向传播所计算出来的损失Loss(即实际值与目标值的差距)。神经网络训练的目的就是在每次迭代(Iteration)中更新可学习参数,使得下次迭代所得到的Loss值越小,即收敛(Convergence)。

我们假定数据层data输出batch size为1,其有2个神经元输出,即数据层形状shape为(1, 2)。连接到全连接层fc7,该层有3个神经元输出,其激活函数为ReLU,其后再接全连接层fc8,它有2个神经元输出,最后连接的输出层为Softmax with Loss层。在监督学习中,数据层还需要有标签(label)输出,也就是ground truth,由全连接层fc8输出为2个神经元我们可知这是一个二分类问题。所以label取值范围为0或1(n分类问题则label取值为0, 1, ..., n -1),我们假定label这里为1。

在演示开始前我们介绍一下什么是ReLU激活函数和Softmax with Loss层,以及他们是如何求导的:

什么是ReLU激活函数

其数学表示为:

其导数为:

注意在x= 0这个点,ReLU函数其实不存在导数的。这是因为在x= +0时,导数为1,而在x= -0时,导数为0。Caffe的实现为当x= -0时其导数为0。

什么是Softmax with Loss层

简单的从字面上理解,Softmax with Loss层即Softmax加上多项式逻辑回归损失函数(multinomial logistic loss)。

Softmax的数学公式为:

它被用来将一个任意取值的n维向量压缩成一个在(0, 1)之间取值的n维向量,并且其和为1。它常被用来计算分类问题中属于某个目标类的概率分布。

而多项式逻辑回归损失函数(multinomial logistic loss)的数学公式为:

即对所有训练样本中取值为目标分类(Label)的概率求负对数再求均值。它用负对数似然(negative log-likelihood)来计算分类问题中分类为实际类别的准确性(分类越准,Loss越低)。因为这里我们batch size为1,所以N为1。因此这里Softmax with Loss的数学表示可以简化为:

注:这里的log是以e为底的对数

细心的同学会发现实际的工程实践中,Softmax with Loss的计算实际上与以上公式有细微的差别,其实际数学公式为:

为什么会有这样的差别呢?从上面的实际公式我们可以发现所有的输入都被减去了最大值才进行常规的softmax计算。这是因为在计算机的浮点运算中,对稍微大一点的浮点数做指数运算(exponent)就会很容易引起上溢(overflow)的(思考题:多大的数会引起上溢?)。对所有输入减去最大值可以确保其为负值,从而避免指数运算上溢的情况出现。

其导数即可通过分别计算softmax和multinominal logistic loss的导数,再运用链式法则得到,也可直接计算softmax with loss的导数。下面我列出后者的导数公式,但在推演时使用前者,有兴趣的读者可以自行验证两者的相等性。

PS:这里(xi - max(xi))' 在对 x求导时,如 i ≠ j,则导数为1,否则导数为0。

数据初值:

设数据层输出为:

[[0.1 0.2]]    #数据输出
[[1]]          #Label输出

设fc7全连接层初始weights为:

[[0.1, 0.2],   #分别对应上图W1_1, W1_2
 [0.3, 0.4],   #分别对应上图W1_3, W1_4
 [0.5, 0.6]]   #分别对应上图W1_5, W1_6

设fc7全连接层初始bias为:

[[0.01, 0.02, 0.03]]    #分别对应上图B1_1, B1_2, B1_3

设fc8全连接层初始weights为:

[[0.1, 0.2, 03],   #分别对应上图W2_1, W2_2, W2_3
 [0.4, 0.5, 0.6]]  #分别对应上图W2_4, W2_5, W2_6

设fc8全连接层初始bias为:

[[0.01, 0.02]] #分别对应上图B2_1, B2_2

前向传播过程推导:

由全连接层性质 y=w0 x+ w1 x1+...+wn xn+b 可知,fc7层的输出为

[[0.06, 0.13, 0.20]] #分别对应上图L1_1, L1_2, L1_3

L1_1=0.1×0.1+0.2×0.2+0.01=0.06

L1_2=0.1×0.3+0.2×0.4+0.02=0.13

L1_3=0.1×0.5+0.2×0.6+0.03=0.20

由ReLU层的性质 y=max(0,x) 可知,ReLU层输出为

[[0.06, 0.13, 0.20]] #分别对应上图Relu1, Relu2, Relu3

Relu1=0.06

Relu2=0.13

Relu3=0.20

同理fc7层,fc8层的输出为

[[0.102, 0.229]]   #分别对应上图L2_1, L2_2

L2_1=0.06×0.1+0.13×0.2+0.20×0.3+0.01=0.102

L2_2=0.06×0.4+0.13×0.5+0.20×0.6+0.02=0.229

最后,softmax w/ loss层中softmax输出S0, S1分别为

[[0.4683, 0.5317]] #分别对应上图S0, S1

softmax w/ loss层中Loss输出为

[0.6317]        #对应上图Loss

Loss=-log⁡(Si│i=Label)=-log⁡(S1)=-log⁡(0.5317)=0.6317

反向传播原理:

神经网络反向传播的主要方法是通过梯度下降(gradient descent)来迭代寻找参数空间最优解以达到最小化Loss函数的目的。因为梯度表示某一函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大。顾名思义,梯度下降即沿梯度下降的方向求解极小值。那么梯度怎么算呢?

我们在神经网络中一般会遇到两种情况:

1) 函数:f:Rn⇒R

如softmax w/ loss层,两个输入S0和S1通过一个函数 f 映射成Loss输出,此时梯度为

2) 函数f:Rn⇒Rm

如fc8全连接层,三个输入Relu1, Relu2和Relu3通过两个不同函数映射成二个输出L2_1和 L2_2,此时梯度为

我们可知当m=1时,第二种情况即可简化为第一种情况。而后者即为Jacobian矩阵

所以我们只需要把每个输出对其所有参数的偏导数按行组合成矩阵,我们将得到雅可比矩阵。然后我们就可以使用复合函数的链式法则

来计算每个可学习参数对最终Loss的影响,即

这里LPij表示第 i 层第 j 个可学习参数,L表示第 i 层输出。这里我们可以知道对某层可学习参数而言,只要知道该层输出对于最终Loss的梯度,以及该可学习参数对该层输出的梯度就可以算出该可学习参数对最终Loss的影响。算出 (即梯度)后,即可对该可学习参数进行更新:

反向传播过程推导:

下面,我们从后向前计算每层梯度。

1) Softmax with Loss层梯度:

如前面所说,为详细解释整个梯度计算过程,我们将分别计算Multinominal logistic loss以及Softmax对其输入的梯度。

[[0, -1.8808]]             #S0与S1对Loss的梯度

[[0.2490, -0.2490],        #L2_1与L2_2对S0的梯度
[-0.2490, 0.2490]]         #L2_1与L2_2对S1的梯度

由此可得

 

[[0.4683, -0.4683]]        #L2层各输入对最终Loss的梯度

2) fc8层梯度:

fc8层共有8个可学习参数(W2_1,W2_2, W2_3, W2_4, W2_5和W2_6,以及B2_1和B2_2)。我们需要计算其对最终Loss的梯度。

因此这些可学习参数对最终Loss的偏导数为

这里我们还需要计算Relu1, Relu2, Relu3对最终Loss的偏导数

3) Relu层梯度

由前面介绍的Relu的导数性质可知,在本例中

所以L1_1, L1_2和L1_3对最终Loss的偏导数为

4) fc7层梯度

由于该层为反向传播最后一层,我们只需要计算该层可学习参数(共9个可学习参数W1_1,W1_2, W1_3, W1_4, W1_5和W1_6,以及B1_1,B1_2和B1_3)对最终Loss的偏导数即可。

所以我们可以算出L1层所有可学习参数对最终Loss的梯度为:

结果验证

在完成以上推演后,我们可以使用Caffe的pycaffe python接口对整个计算过程进行验证。在Caffe中定义该网络拓扑结构极为简单,用户只需要输入以下内容并保存在一个文本文件即可。

#file name "bp.pt"
name: "BackPropagation"
layer {
  name: "data"
  type: "DummyData"
  top: "data"
  top: "label"
  dummy_data_param {
    data_filler { type: "constant" value: 0.01 }
    shape: { dim: 1 dim: 2 }
    shape: { dim: 1 dim: 1 }
  }
}
layer {
  name: "fc7"
  type: "InnerProduct"
  bottom: "data"
  top: "fc7"
  inner_product_param { num_output: 3 }
}
layer {
  name: "relu7"
  type: "ReLU"
  bottom: "fc7"
  top: "fc7"
}
layer {
  name: "fc8"
  type: "InnerProduct"
  bottom: "fc7"
  top: "fc8"
  inner_product_param { num_output: 2 }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "fc8"
  bottom: "label"
  top: "loss"
}
相应的python代码如下
# file name "bp.py"

import caffe
import numpy as np

def datafiller(net):
        '''该函数用来初始化数据层数据,以及fc7和fc8层初始权重与偏差'''
        net.blobs['data'].data[...] = np.array([0.1, 0.2])
        net.blobs['label'].data[...] = np.array([1.0])
        # fc7 weight shape is (3,2), bias shape is (3,)
        net.params['fc7'][0].data[...] = np.array([[0.1, 0.2],
                                                   [0.3, 0.4],
                                                   [0.5, 0.6]])
        net.params['fc7'][1].data[...] = np.array([0.01, 0.02, 0.03])
        # fc8 weight shape is (2, 3), bias shape is (2, )
        net.params['fc8'][0].data[...] = np.array([[0.1, 0.2, 0.3],
                                                   [0.4, 0.5, 0.6]])
        net.params['fc8'][1].data[...] = np.array([0.01, 0.02])

if __name__ == '__main__':
        caffe.set_mode_cpu()
        net = caffe.Net("bp.pt", caffe.TRAIN)
        datafiller(net)
        loss = net.forward()
        net.backward()
        print "loss is {}".format(loss['loss'])
        print net.params['fc8'][0].diff   #fc8 layer's weight derivative w.r.t loss
        print net.params['fc8'][1].diff   #fc8 layer's bias derivative w.r.t loss
        print net.params['fc7'][0].diff   #fc7 layer's weight derivative w.r.t loss
        print net.params['fc7'][1].diff   #fc7 layer's bias derivative w.r.t loss
        print net.blobs['fc8'].diff       #fc8 output derivative w.r.t loss
        print net.blobs['fc7'].diff       #fc7 output derivative w.r.t loss

在Caffe中,net.params对象包含了该网络拓扑中所有可学习参数对最终Loss的梯度,而net.blobs对象则包含了每层输出的数据对最终Loss的梯度。读者可通过以上打印输出验证结果的正确性。

知识扩展:如何计算卷积层的梯度?

如果有以下卷积层,读者知道如何计算卷积层的梯度吗?

如上图,我们假定该卷积层输入NCHW格式为(1, 1, 3, 3),通过一个2x2的卷积核,我们将得到一个输出NCHW格式为(1, 1, 2, 2)的输出。它是通过以下公式得到的:

a11×w11+a12×w12+a21×w21+a22×w22+B=o11 a12×w11+a13×w12+a22×w21+a23×w22+B=o12 a21×w11+a22×w12+a31×w21+a32×w22+B=o21 a22×w11+a23×w12+a32×w21+a33×w22+B=o22

由以上公式我们可知该层可学习参数对输出的Jacobian矩阵为

该层输入对输出的Jacobian矩阵为

由以上两个公式我们就可以推导出该层输入对最终Loss的梯度了:

从该公式我们可知,该层输入对最终Loss的梯度为该层输出对最终Loss的梯度与其权重的卷积(即该层输出对最终Loss的梯度做为卷积核,在权重wij上做卷积,其pad为1)。

该层可学习参数对最终Loss的梯度为:

同理可知,该层权重对最终Loss的梯度为该层输出对最终Loss的梯度与其权重的卷积(即该层输出对最终Loss的梯度做为卷积核,在数据输入aij上做卷积,其pad为1)。

到此,本篇科普文章就要结束了,希望能对大家了解神经网络的运行机制有所帮助。

参考文献

Intel Caffe: https://github.com/intel/caffe

Caffe Tutorial: http://caffe.berkeleyvision.org/tutorial

Matrix Calculus: https://arxiv.org/abs/1802.01528

Backpropagation: https://brilliant.org/wiki/backpropagation

Jacobian Matrix and Determinant: https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant

关于作者

田丰(feng.tian@intel.com)是英特尔软件与服务事业部DPD/MLT Intel Caffe开发团队的一员,专注于Caffe框架在Intel Xeon/Xeon phi上的优化。

使用 Unity* 进行并行处理的一种方法

$
0
0

项目理念

该项目的理念是展示如何使用 Unity* 对游戏进行并行处理,以及如何使用游戏引擎执行与游戏相关的物理。在这个领域内,现实感是成功的一个重要标志。为了模拟真实世界,许多动作需要同时发生,这需要并行处理。创建两个不同的应用,然后将它们与单个内核上运行的单线程应用进行比较。

第一个应用在多线程 CPU 上运行,第二个应用在 GPU 上执行物理计算。为了显示这些技术的结果,新开发的应用展示了使用群集算法创建的鱼群。

群集算法

多数群集算法依赖 3 大规则:


图 1.3 大群集规则描述(资料来源:http://www.red3d.com/cwr/boids/)。

什么是群集?

在本示例中,一个群集被定义为一群鱼。如果每条鱼与鱼群里的其他鱼都保持一定的距离,经过计算得出,该鱼在一个鱼群里“游动”。鱼群的成员只能以群集成员的身份行动,不得单独行动,它们共享相同的参数,如速度和方向。


图 2.包含 4 条鱼的鱼群。

复杂度

该算法的复杂度为 O(n2),其中,n 为鱼的数量。为了更新单条鱼的移动,算法需要查看环境中的所有 n 条鱼,以确定鱼是否可以:1) 留在鱼群;2) 离开鱼群;或者 3) 加入新鱼群。单条鱼可能单独“游”一段时间,并有机会加入新鱼群。这需要针对每条鱼执行 n 次。

算法如下所示:

For each fish (n).

Look at every other fish (n).

If this fish is close enough.

Apply rules: Cohesion, Alignment, Separation.

使用 C# 实施群集算法

为了将规则应用于每条鱼,创建了一个 Calc函数,它需要一个参数:环境中现有鱼的索引。

数据被存储于两个缓冲区,以表示每条鱼的状态。交替使用这两个缓冲区进行读写。需要这两个缓冲区在内存中保存每条鱼之前的状态。然后,使用该信息计算每条鱼的下一个状态。每一帧前,读取当前读取缓冲,以更新场景。


图 3.功能流程框图。

鱼的状态

每条鱼的状态包括:

fishState {
	float speed;
	Vector3 position, forward;
	Quaternion rotation;
}


图 4.包含 fishState 的鱼。

forward变量包含了鱼面对的方向。

rotation变量是一个表示三维旋转的四元数,支持鱼旋转以面对目标方向。

群集算法

本项目使用的完整的群集算法为:

邻近状态

执行群集算法后,每条鱼都会被确认为鱼群成员或非鱼群成员。

将两条鱼之间的距离和每条鱼的前进方向用作参数,创建 neighbor 函数。这样做旨在使行为更逼真。如果两条鱼的距离足够小,并且沿着相同方向行进,它们有可能合并。但是,如果它们没有沿相同的方向移动,便不太可能合并。使用分段二次函数和前向矢量的点积创建该合并行为。


图 5.数学函数的表示。

两条鱼之间的距离必须小于最大距离,后者根据前向矢量的点积不断变化。

调用繁重的 Neighbor函数前,需要调用另一个函数:Call。Call函数会通知算法是否需要通过 Neighbor函数来确定两条鱼是否足够接近,能进入同一个鱼群。Call函数只检查这些元素(鱼)相对其 x 位置的位置。x 位置是首选位置,因为它拥有最宽的尺寸,支持鱼广泛分布。

状态更新

如果只有 1 条鱼,它会在特定速度范围内沿某个方向前进。如果鱼周围有同伴,它将调整自己的方向和速度,以适应鱼群的方向和速度。

速度总是以线性的方式变化,以提高平滑度。速度不会在没有过渡的情况下跳至另一级。

我们界定了一个环境。不允许鱼游到该环境的空间范围之外。如果鱼触碰了边界,将转向,并回到界定的环境中。


图 6.群集行为。

如果鱼将要游出边界,为鱼随机指定一个新的方向和速度,使其仍在界定环境以内。


图 7.边界行为。

检查鱼是否会撞上岩石也十分必要。该算法需要计算鱼的下一个位置是否在岩石内。如果是,鱼将以类似于避开边界的方式避开岩石。

一旦计算出状态,将指示所有鱼开始“游动”,并进行必要的旋转。然后,更新每条鱼的下一个状态,如方向、速度、位置和旋转变量(面向 n 条鱼)。该操作在每次更新帧时发生。

例如,为所有鱼的位置添加了方向,以确保鱼在环境内“游动”。

Unity* 内的集成

Unity 内的主要编程组件为 GameObject。您可以在 GameObject 内添加不同的元素,如待执行的脚本、碰撞器、纹理或材料,以自定义对象,使其按预想的方式运行。然后,可以在 C# 脚本内便捷地访问这些对象。脚本内的每个公共对象将在编辑器内创建一个字段,支持您放置满足要求的对象。

使用 C# 脚本创建群集行为。

将资产导入 Unity

  1. 单击 Assets,,单击 Import package,然后单击 Custom package。 
  2. 单击 All。
  3. 单击 Import。

接下来,将 Main场景从 Project 选项卡拖放到 Hierarchy 选项卡。右键单击默认场景并选择“Remove Scene”。

运行应用所需的所有游戏对象和附带的脚本均可以随时运行。唯一缺少的部分是岩石模型,这个必须手动添加。

从 Unity 资产商店下载“Yughues Free Rocks”。可以在 Unity 内访问资产商店(或使用该链接:http://u3d.as/64P访问)。下载完成后,将出现左侧的窗口。选择“Rock 01”并导入。

由于默认模型的规模太大,需要在使用岩石模型前进行调整。网格导入设置的缩放系数应重新调整为 0.0058。将岩石添加到场景后,如果其规模为 1,将匹配比例为 1 的三维球体,后者被用作面向对象的碰撞器。


图 13.分割矩阵阵列-计算变量。

计算变量:nbmat表示应用使用的矩阵队列数量;rest表示最后一个矩阵中鱼的数量。

更新每条鱼:表示矩阵索引;j表示当前数据块内鱼的索引。只有这样做,才能更新上述阵列内的正确矩阵。

其他特性

水下效果

为了创建多种水下效果,在 Unity 项目中添加了不同的资产。Unity 提供了多款内置软件包,包括本项目使用的水模型。还提供了适用于任意对象的多种纹理和相关材料(“皮肤”)。上述全部(甚至更多)资产可在 Unity 资产商店中获取。

焦散-光反射与阴影

在该项目的水下场景中添加了焦散光照效果。焦散需要一个“投影器”(projector,一种 Unity 对象)。投影器显示场景中的焦散效果。通过假设特定的频率(Hz)改变投射的焦散,提供一种焦散正在移动的效果。

模糊

在水下场景中添加了模糊效果。如果摄像头位于水平面以下,将启用并显示渐进模糊。场景的背景将变为蓝色。默认背景为天空背景(天空盒)。此外,在 Unity 内启动了雾设置。(Microsoft Windows*, 光照,其他设置,已检查雾盒)。

移动摄像头

在摄像头对象中添加脚本,以支持使用键盘和鼠标在场景内移动。这种操作提供了类似于第一人称射击游戏的控制。可以使用方向键进行前进/后退、向左/向右扫射。鼠标支持上/下移动,以及转动摄像头,以对准左侧/右侧。

transform.Rotate (0, rotX, 0);

move变量表示方向键输入,rot* 表示鼠标方向。修改保留脚本的对象(在本示例中为摄像头)的转换,使其在场景中旋转与平移。

创建 .exe 文件

如前所述,在不修改源代码的情况下,可以构建一个 .exe 文件,以改变应用的参数。请按照以下步骤操作:

  1. 单击:Edit ,并单击  Project Settings ,然后单击  Quality。
  2. Quality 选项卡中,向下滚动至 Other,,并找到 V Sync Count
  3. V Sync Count设置改为 “Don’t Sync”。这可能使应用显示 60fps 以上的帧速率。
  4. 单击:File ,然后单击  Build and Run,以创建 .exe 文件。

注:除了使用 Build and Run, 之外,您也可以访问 Build Settings ,以选择特定平台(如 Microsoft  Windows、Linux*、Mac* 等)。

编码差异:CPU 对比 GPU

CPU

面向单线程和多线程应用的编码之间只包含一个差异:Calc 函数的调用方式。在本示例中,Calc 函数对执行时间至关重要,因为每一帧它都被调用 n 次。

单线程

如下所示,通过一个“for 循环”以传统的方式完成面向单线程应用的编码:

多线程

使用“Parallel.For”类完成面向多线程应用的编码。Parallel.For 类的作用是分割多个函数调用,并在不同的线程中并行执行这些调用。每个线程包含需要执行的多个调用。当然,应用性能取决于 CPU 可用内核数量。

GPU

计算着色器

GPU 处理以类似于 CPU 多线程处理的方式完成。通过将流程繁杂的 Calc 函数迁移至 GPU(GPU 的内核数量多于 CPU),将更快地预测结果。为此,在 GPU上使用并执行“着色器”。着色器为场景添加图形效果。本项目使用了“计算着色器”。使用 HLSL(高级着色器语言)编写计算着色器。计算着色器复制Calc函数的行为(如速度、位置、方向等),无需计算旋转。

使用 Parallel.For函数的 CPU 调用面向每条鱼的 UpdateStates函数,以便在绘制每条鱼前,计算其旋转并创建 TRS 矩阵。使用“四元数”类的 Unity 函数 Slerp计算鱼的旋转。

根据计算着色器调整代码

尽管主要思路是将 Calc 函数循环迁移至 GPU,仍需要考虑以下几点:随机数生成以及需要和 GPU 交换数据。

面向 CPU 的 Calc函数与面向 GPU 的计算着色器之间最大的差异在于随机数生成。在 CPU 中,使用来自 Unity 随机类的对象生成随机数。计算着色器中使用的是 NVidia* DX10 SDK 函数。

需要在 CPU 和 GPU 之间交换数据。

某些应用参数(如鱼或岩石的数量)包含在浮点矢量或单个浮点中。例如,CPU 中来自 C# 的 Vector3 将匹配 GPU 上以 HLSL 编写的 float3 的内存映射。

读/写缓冲中的鱼状态数据(fishState)和 CPU 中第三个缓冲区上的岩石状态数据(s_Rock)必须在 GPU 上被定义为计算着色器的 3 个不同的 ComputeBuffer。例如,CPU 上的四元数匹配 GPU 上 float4 的内存映射。(四元数是一种包含 4 个浮点的结构。)读/写缓冲在 GPU 上的计算着色器中被声明为 RWStructureBuffer <State>。在 CPU 上描述岩石的结构与之类似,使用浮点表示每块岩石的大小,使用包含 3 个浮点的矢量表示每块岩石的位置。

CPU 上的 RunShader函数创建了 ComputeBuffer 状态,并调用 GPU,使其在每帧开始时执行计算着色器。

<img data-fid="613900" data-cke-saved-src="/sites/default/files/managed/b7/7a/An-Approach-to-Parallel-Processing-with-Unity-code09_0.png” typeof=" src="/sites/default/files/managed/b7/7a/An-Approach-to-Parallel-Processing-with-Unity-code09_0.png” typeof=" foaf:image"="">

在 CPU 上创建 ComputeBuffer 状态后,对它们进行设置,以匹配 GPU 上相关的缓冲状态(例如,CPU 上的读取缓冲与 GPU 上的“readState”相关)。然后,使用鱼状态数据对两个空缓冲区进行初始化,执行计算着色器,使用与写入缓冲相关的 ComputeBuffer 中的数据更新写入缓冲。

在 CPU 上,Dispatch 函数设置并启动 GPU 上的线程。nbGroups表示在 GPU 上执行的线程组数。在本示例中,每组包含 256 个线程(一个线程组所包含的线程数不能超过 1,024 个)。

在 GPU 上,“numthreads” 属性必须与 CPU 上建立的线程数一致。如下所示,“int index = 16*8*8/4”提供了 256 个线程。需要将每个线程的索引设置为每条鱼相应的索引,每个线程需要更新每条鱼的状态。

结果


图 14.鱼数量较少时的结果。

结果显示,当鱼的数量少于 500 条时,相比 GPU,单线程与多线程 CPU 的性能更高。这可能得益于在 CPU 与 GPU 之间逐帧完成的数据交换。

当鱼的数量达到 500 条时,相比多线程 CPU 和 GPU,单线程 CPU 的性能降低(单线程 CPU = 164fps 对比多线程 CPU = 295fps,GPU = 200fps)。当鱼的数量达到 1,500 条时,多线程 CPU 的性能降低(单线程 CPU = 23fps,多线程 CPU = 88fps 对比 GPU = 116fps)这可能是因为 GPU 拥有更多内核。

当鱼的数量大于等于 1500 条时,GPU 的性能在所有情况下均优于单线程和多线程 CPU。


图 15.鱼数量较多时的结果。

尽管在所有情况下,GPU 的性能优于两个 CPU 实例的性能,结果显示当鱼的数量为 1,500 条时,整体 GPU 性能最佳(116fps)。随着鱼数量的继续增加,GPU 性能随之下降。即便如此,当鱼的数量为 2000 条时,只有 GPU 的性能超过 60fps,鱼的数量为 2500 条时,GPU 的性能超过 30fps。当鱼的数量约为 6500 条时,GPU 最终降至 30fps 以下。

随着鱼数量的增加,GPU 性能下降的最可能的原因是算法复杂性。例如,当鱼的数量为 10,000 条时,每条鱼需要进行 10,0002 ,即 1 亿次迭代,以在每一帧中与其它鱼进行交互。

对应用进行分析后,需要强调应用内的几个关键点。用于计算每条鱼距离的函数非常繁重,点积导致 Neighbor函数速度变慢。将 Neighbor调用替换为两条鱼之间的距离(必须小于最大距离)将小幅提升性能。这意味着邻近的任意两条鱼现在将向同一个方向游动。

关注算法的 O(n2)复杂度是提升性能的另一种方法。面向鱼的替代整理算法可能会改进性能。

(假设两条鱼:f1 和 f2。面向 f1 调用 Calc函数后,将针对 f2 计算 f1 的 Neighbor状态。保存 Neighbor状态值,以在稍后面向 f2 调用 Calc 函数时使用。)

该性能指标评测使用的硬件


图 16.运行测试所用的硬件。

Viewing all 583 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>