TensorFlow Unreal 插件
Unreal Engine 插件用于 TensorFlow。 使得在你的虚幻项目中训练并实现最先进的 机器学习 算法。
这个插件包含封装了TensorFlow操作的C++,Blueprint 和 python 脚本作为一个 Actor Component。它依赖于 UnrealEnginePython 插件分支和 SocketIO Client 插件; 这些总是包含在 binary releases 中,因此不需要手动外部下载。有关实现和架构的详细信息,请参阅 Note on Dependencies 部分。
讨论见 unreal 论坛线程。
问题和限制
目前只有一个适用的 Windows 平台的工作版本。请注意项目的位置,因为你可能会遇到 python 依赖路径字符数限制 240。
近期重构以开放开发环境和本地支持(进行中): https://github.com/getnamo/TensorFlow-Unreal/issues/53
- 机器学习远程 - https://github.com/getnamo/MachineLearningRemote-Unreal
- Tensorflow 本地 - 专注于推理(进行中) https://github.com/getnamo/TensorFlowNative-Unreal
Tensorflow UnrealEnginePython 平台问题
-
Android 问题#11 跟踪 - 可能会被 tf native 取代
-
Mac OS 问题#10 跟踪 - 可能会被 ml remote 取代
如果你有点子或者修复,考虑贡献!见 https://github.com/getnamo/TensorFlow-Unreal/issues 获取当前问题。
安装与设置
- (仅限 GPU) 安装 CUDA 和 cudNN 先决条件 如果你使用兼容的 GPU(NVIDIA)
- 下载最新版发布 选择 CPU 或 GPU 版本下载,如果支持。
- 创建新的或选择项目。
- 浏览到你的项目文件夹(通常位于 Documents/Unreal Project/{Your Project Root})
- 复制 Plugins 文件夹到你的项目根目录。
- 启动你的项目。
- (可选) 所有插件应默认启用,你可以通过 Edit->Plugins 确认。向下滚动到 Project,你应该看到三个插件,计算中的 TensorFlow,联网中的 Socket.IO Client 和 脚本语言中的 UnrealEnginePython。如果有任何禁用状态,点击启用并重启编辑器,再次打开你的项目。
- 等待 tensorflow 依赖自动安装。它会自动解决 Content/Scripts/upymodule.json 中列出的所有依赖项使用 pip。注意此步骤可能需要几分钟,取决于你的互联网连接速度,直到完全完成你将不会在输出日志窗口中看到任何内容。
- 一旦你看到类似的输出(每个版本的tensorflow的特定包会变化),插件就可以使用了。
关于 Git 克隆的注意事项
推荐使用完整的 插件二进制发布,这使你可以按照 如上安装指示 并迅速上手。
如果你愿意自己 git 克隆和手动同步到主仓库,建议你 下载最新的 python 二进制依赖发布 用于 UnrealEnginePython。 这包含一个嵌入的 python 构建;从下载中选择 BinariesOnly-.7z 文件并将插件文件夹拖到你的项目根目录。完成该步骤后,你的克隆仓库应该按预期工作,所有其他依赖项将在第一次启动时通过 pip 拉取。
示例
基础 MNIST softmax 分类器在开始游戏时训练,训练输入样本在训练期间流到编辑器。完全训练后,UTexture2D (1-3) 样本被测试进行预测。
一个示例项目在 https://github.com/getnamo/TensorFlow-Unreal-examples。
该仓库有一般 tensorflow 控制的基本示例和不同的 mnist 分类示例,使用 UE4 的 UTexture2D 输入进行预测。随着更多即插即用示例的创建,仓库应会扩展。考虑通过 pull 请求贡献样本!
它也是该插件所有依赖项开发的主仓库。
Python API
你可以直接在 UE4 中训练或使用训练好的模型。
首先,将你的 python 脚本文件添加到 {Project Root Folder}/Content/Scripts。
通过继承 TFPluginAPI 包装你的 tensorflow python 代码。
MySubClass(TFPluginAPI)
import tensorflow
, unreal_engine
和 TFPluginAPI
在你的模块文件并用以下函数继承 TFPluginAPI 类。
import tensorflow as tf
import unreal_engine as ue
from TFPluginAPI import TFPluginAPI
class ExampleAPI(TFPluginAPI):
#预期可选api:为训练设置你的模型
def onSetup(self):
pass
#预期可选api:解析输入对象并返回一个结果对象,这将被转换为 json 供 UE4 使用
def onJsonInput(self, jsonInput):
result = {}
return result
#预期可选api:开始训练你的网络
def onBeginTraining(self):
pass
#注意: 这是一个模块函数,而不是一个类函数。更改你的类名以反映你的类
#需要的函数来获取我们的api
def getApi():
#返回 类名.getInstance()
return ExampleAPI.getInstance()
注意 getApi()
模块函数需要返回一个与你定义的类实例匹配的实例。其余的功能取决于你要为你的用例使用的 API。目前该插件支持通过 JSON 编码从 UE4 输入/输出。
如果你想在 UE4 中训练,请在 onBeginTraining()
中实现你的逻辑,并确保你在每个批次/每个周期后检查 self.shouldStop
以处理用户的早期退出请求,例如当你 EndPlay 或者手动调用 tensorflow 组件上的 StopTraining
时。你还将收到一个可选的 onStopTraining
回调,当用户停止你的训练会话时将调用。
如果你有一个训练好的模型,只需在设置时加载/设置你的模型并省略训练功能,并通过 onJsonInput(jsonArgs)
回调转发评估/输入。见 mnistSaveLoad.py 示例 如何训练一个网络一次,然后保存模型,在设置时重新加载它,这样你每次都跳过重新训练。
请注意,onBeginTraining()
和 onSetup()
默认是异步调用的。如果你使用像 keras 这样的高级库,可能需要分别存储你的 tf.Session 和 tf.Graph 并将其作为默认值使用 with self.session.as_default():
和 with self.graph.as_default():
来评估,因为所有调用通常都会在单独的线程中完成。
以下是一个非常基本的示例,使用 tensorflow 添加或减去传入的值如 {"a":<浮点数或数组>, "b":<浮点数或数组>}
。
import tensorflow as tf
import unreal_engine as ue
from TFPluginAPI import TFPluginAPI
class ExampleAPI(TFPluginAPI):
#预期可选api:为训练设置你的模型
def onSetup(self):
self.sess = tf.InteractiveSession()
self.a = tf.placeholder(tf.float32)
self.b = tf.placeholder(tf.float32)
#操作
self.c = self.a + self.b
pass
#预期可选api:以 python 对象格式输入json,获取 a 和 b 值作为 feed_dict
def onJsonInput(self, jsonInput):
#在日志中显示我们的输入
print(jsonInput)
#将传入的值映射到输入占位符
feed_dict = {self.a: jsonInput['a'], self.b: jsonInput['b']}
#运行计算并获得结果
rawResult = self.sess.run(self.c, feed_dict)
#转换为数组并将答案嵌入为python对象中的'c'字段
return {'c':rawResult.tolist()}
#自定义函数改变操作类型
def changeOperation(self, type):
if(type == '+'):
self.c = self.a + self.b
elif(type == '-'):
self.c = self.a - self.b
#预期可选api:此示例中我们不进行任何训练
def onBeginTraining(self):
pass
#注意: 这是一个模块函数,而不是一个类函数。更改你的类名以反映你的类
#需要的函数来获取我们的api
def getApi():
#返回 类名.getInstance()
return ExampleAPI.getInstance()
一个完整的使用 mnist 的示例见此:https://github.com/getnamo/TensorFlow-Unreal-examples/blob/master/Content/Scripts/mnistSimple.py
一个完整的使用保存/加载设置的示例见此:https://github.com/getnamo/TensorFlow-Unreal-examples/blob/master/Content/Scripts/mnistSaveLoad.py
另一个使用 keras api 的完整示例见此:https://github.com/getnamo/TensorFlow-Unreal-examples/blob/master/Content/Scripts/mnistKerasCNN.py。注意 keras 回调用于在当前批次完成后停止训练,这在游戏玩法提前结束时取消训练,例如 EndPlay。
异步事件到 Tensorflow 组件
如果你需要在训练期间向 blueprint 流数据,可以使用 self.callEvent()
api。
字符串格式
格式为 self.callEvent('EventName', 'MyString')
Json 格式
格式为 self.callEvent('EventName', PythonObject, True)
示例用例在 mnistSpawnSamples.py,训练图像样本被发射到虚幻进行预览。
Blueprint API
从你的 TensorflowComponent 加载你的 python 模块
一旦你 编写了你的 python 模块,选择你的 actor blueprint 中的 TensorflowComponent
并将 TensorFlowModule 名称更改为反映你的 文件名,不带 .py。 例如,如果我的 python 文件是 ExampleAPI.py,它会像这样:
可选地禁用 python 详细日志并更改其他选项,如在 BeginPlay 训练或禁用多线程(不推荐)。
训练
默认情况下 onBeginTraining() 函数将在组件的 begin play 调用时被调用。 你可以选择不勾选此选项并手动调用 Begin Training。
向你的模型发送 Json 输入进行预测
你可以控制传递到你的 python 模块的数据类型当前 api 的唯一限制是它应为 JSON 格式。
基本 Json 字符串
在最简单的情况下,你可以发送例如基本 json 字符串 {"MyString","SomeValue"}
使用 SIOJson 构造如下
任何 UStruct 示例
SIOJson 支持完全用户定义的结构体,即使是仅在蓝图中定义的。高度推荐使用此类结构体作为组织数据的便捷方式,并可靠地在 python 端解码。以下是一个示例,我们发送自定义bp结构并将其直接编码为JSON。
结构体在蓝图中的定义如下
你还可以交织结构体,甚至是常见的虚幻类型,所以请随意混搭上述两种方法。在此特定示例中,我们在定义的 json 对象中交织一个3D向量。发送的输入应为 {"SomeVector":{"x":1.0,"y":2.3,"z":4.3}}
特殊便利情况:UTexture2D
一个便捷函数将 UTexture2D 包装为 json 对象 {"pixels":[<1D 像素数组>], "size":{"x":<图像宽度>,:"y":<图像高度>}}
你可以使用 numpy 进行重新塑造。
注意,这目前会将图像转换为完整的 alpha 灰度图像。如果你需要彩色纹理输入,自行定义方法或提交 pull 请求。
自定义函数
如果你需要从蓝图调用 python 函数而当前api不支持的,可以使用 TensorflowComponent 上的 CallCustomFunction
方法。你指定函数名并传入字符串作为参数。函数在游戏线程上运行并将立即返回一个预期的字符串值。对于参数和返回值,建议使用 JSON 编码,但也是可选的。
示例自定义函数调用传递一个字符串参数到 changeOperation
在 addExample.py
处理 Tensorflow 事件
从你的 actor 蓝图选择你的 Tensorflow Component,然后在事件图中点击 + 订阅所选事件。
当前api支持以下事件
On Input Results
当 onJsonInput() 在你的python模块中完成时调用。返回的数据是你在函数末尾传递的返回数据的json字符串。
通常你想要将这个字符串转换为 SIOJsonObject 以便在蓝图中使用你的结果数据。对于分类任务,通常会附有一个预测字段。
如果你有一个常规返回格式,可以考虑制作自己的自定义bp结构并从json字符串填充其值,如下所示
注意函数将仅填充具有匹配名称的字段并忽略所有其他结构字段。 这意味着你可以安全地从一个比结构体定义的字段更多的json字符串中填充部分结构。
On Training Complete
当 onBeginTraining() 调用完成时,你会收到这个事件和 {'elapsed':<所花时间>}
json,可选地附加你函数中传递的返回数据。
On Event
如果你使用 self.callEvent()
你会收到这个事件派发。你可以通过事件名称过滤你的事件类型,然后根据传递的数据进行处理。
例如 mnistSpawnSamples.py 使用 self.callEvent()
异步流训练图像,我们会通过检查 'PixelEvent'
来过滤。
Blueprint 实用工具
转
<SOURCE_TEXT> C++
static UTexture2D* Conv_FloatArrayToTexture2D(const TArray<float>& InFloatArray, const FVector2D Size = FVector2D(0,0));
转换为Texture2D(渲染目标2D)
将UTextureRenderTarget2D转换为UTexture2D
蓝图
转换为Texture2D(渲染目标2D)
C++
static UTexture2D* Conv_RenderTargetTextureToTexture2D(UTextureRenderTarget2D* InTexture);
转换为浮点数组(字节)
将一个字节数组转换为一个浮点数组,并由传入的比例标准化
蓝图
转换为浮点数组(字节)
C++
static TArray<float> Conv_ByteToFloatArray(const TArray<uint8>& InByteArray, float Scale = 1.f);
TF音频捕捉组件
一个使用Windows API无需在线子系统来捕捉和流式传输麦克风音频的C++组件。有关API的详细信息,请参见https://github.com/getnamo/TensorFlow-Unreal/blob/master/Source/TFAudioCapture/Public/TFAudioCaptureComponent.h。
该组件旨在用于当TensorFlow示例成熟时的本机语音识别。
文件实用组件
一个简单的蓝图包装器,用于从文件保存和加载字节。允许轻松刷新诸如音频捕捉用于以后的使用。有关API的详细信息,请参见https://github.com/getnamo/TensorFlow-Unreal/blob/master/Source/CoreUtility/Public/FileUtilityComponent.h。
使用pip在Python控制台管理您的依赖项
该插件使用一个pip包装脚本,该脚本使用子进程以使不阻塞行为。只需在您的脚本中导入它
import upypip as pip
然后输入例如:
pip.list()
这应该能很快列出您已安装的所有Python模块。
包名 版本
------------ ---------
absl-py 0.1.10
astor 0.6.2
bleach 1.5.0
gast 0.2.0
grpcio 1.10.0
html5lib 0.9999999
Markdown 2.6.11
numpy 1.14.1
pip 9.0.1
protobuf 3.5.1
setuptools 38.5.1
six 1.11.0
tensorboard 1.6.0
tensorflow 1.6.0
tensorflow-gpu 1.6.0
termcolor 1.1.0
Werkzeug 0.14.1
wheel 0.30.0
如果您想添加另一个模块,请调用安装函数。例如,如果您想升级到gpu版本,您可以简单地输入
pip.install('tensorflow-gpu')
或者如果您想回到一个干净的状态,可以
pip.uninstallAll()
这应该只留下基本的
包名 版本
---------- -------
pip 9.0.1
setuptools 38.5.1
wheel 0.30.0
有关所有可用命令,请参见upypip.py。
关于依赖项的注意事项
依赖于一个UnrealEnginePython插件分支和SocketIO客户端插件。这两者和一个嵌入式Python构建都包含在每个发布中,因此您不需要手动包含任何内容,只需从任何发布中将Plugins文件夹拖到您的项目中。
架构与目的
UnrealEnginePython
基于20tab的精彩工作,UnrealEnginePython插件分支包含更改,以启用多线程、Python脚本插件封装和通过pip自动解决依赖关系。只需在https://github.com/getnamo/TensorFlow-Unreal/blob/master/Content/Scripts/upymodule.json中指定Tensorflow为_pythonModule_依赖项,该编辑器将在首次运行时自动解决依赖关系。多线程支持包含一个回调系统,允许长时间操作在后台线程上发生(例如训练),然后在您的游戏线程上接收回调。这使得TensorFlow能够在不明显影响游戏线程的情况下工作。
SocketIO 客户端
SocketIO客户端用于通过JSON在本机引擎类型(BP或C++结构和变量)和Python对象之间进行便捷转换。可以选择用于通过socket.io连接到实时网络服务。
打包
仅蓝图项目的注意事项
在打包之前,您需要将您的仅蓝图项目转换为混合(蓝图和C++)。按此说明进行操作:https://allarsblog.com/2015/11/04/converting-bp-project-to-cpp/
额外步骤
自v0.10.0起,该插件应能正确打包,但需要运行打包后的构建一次以拉取依赖项。您可以选择从{Project Root}/Plugins/UnrealEnginePython/Binaries/Win64/Lib/site-packages
手动将它们复制到打包文件夹{Packaged Root}/{Project Name}/Plugins/UnrealEnginePython/Binaries/Win64/Lib/site-packages
。
当您首次启动打包项目时,可能会有一段时间的黑屏(2分钟),因为它会重新安装pip并首次拉取依赖项。几分钟后重新加载地图或重新启动(查看您的打包日志以了解何时准备好)。从那以后,每次启动项目时应该快速加载。请注意,您可以将打包项目打包并移动到另一台计算机上,并包含所有依赖项,但首次运行时仍会有约20秒的启动时间,因为它会重新安装pip到正确的位置,但无需拉取pip依赖项,从而节省大部分等待时间,之后每次启动将快速。
故障排除/帮助
我看到从升级TensorFlow版本的pip错误
删除Plugins\UnrealEnginePython\Binaries\Win64\Lib\site-packages
并重启项目
没有名为'tensorflow'的模块
首次运行时,您可能会在Python控制台中看到此消息
等待pip完全安装您的依赖项,这可能需要约3-5分钟。当依赖项安装完成时,它应该看起来像这样
看到此显示后,关闭编辑器并重新启动项目。再次启动项目后,不应再显示此错误。
开始播放时2-3秒的卡顿
这是由于在开始播放时Python导入TensorFlow并加载所有dll。目前不可避免,每次编辑器启动时仅发生一次。
发现未列出的问题?
将您的问题发布到https://github.com/getnamo/TensorFlow-Unreal/issues。
许可证
插件 - MIT
TensorFlow和TensorFlow图标 - Apache 2.0 </SOURCE_TEXT>