技术拆解-Unity神经网络可视化

场景结构

MainCamera

上面挂了一些系统内置的脚本,比如 HDAdditionalCameraData.cs

也有自定义的脚本:SimpleCameraController.cs

还有个自定义脚本,在运行过程中根据用户交互动态挂载/卸载:CameraRotationScript.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraRotationScript : MonoBehaviour
{
public GameObject centerObject;
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
transform.LookAt(centerObject.transform.position + new Vector3(0, 0.25f, 0), Vector3.up);
transform.RotateAround(centerObject.transform.position , Vector3.up, 15 * Time.deltaTime);
}
}

Display

DisplayFlat

这个对应的是显示数字的那个面板,即输入源。

逻辑是不停读取 Texture,在每一帧里面将其展开,显示出来。

该对象挂载了 FlattenTexture.cs 脚本

NetRunner

挂了一个 NetRunnerBehavior.cs 脚本

这里设置了一些属性,可以在 Editor 中设置,比如:

1
public NNModel modelSource;

这种设计,很好的解决了配置文件的管理问题,比我们现在手动设置到文件中更好操作。

层和线条都是在这里绘制的。

比如绘制线条:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private GameObject drawLine(Vector3 startPos, Vector3 endPos,Color color,bool startActive=false)
{
var lineObject = new GameObject("Edge_" + startPos.ToString());
var edgeParent = GameObject.Find("Edges");
if (edgeParent != null)
lineObject.transform.SetParent(edgeParent.transform);
var line = lineObject.AddComponent<LineRenderer>();
//line.positionCount = 2;
//line.SetPositions(new Vector3[] { startPos, endPos });
Vector3 midPoint = Vector3.Lerp(startPos, endPos, 0.5f) + new Vector3(Random.Range(-0.2f, 0.2f), Random.Range(-0.2f,0.2f),0);
DrawQuadraticBezierCurve(line, startPos, midPoint, endPos);
line.startWidth = 0.005f;
line.endWidth = 0.005f;

var mat = Resources.Load("LineMat") as Material;

//mat.SetColor("Base Color", color);
line.material = mat;
//line.material = new Material(Shader.Find("Sprites/Default"));
line.startColor = new Color(1,1,1,0.1f);
line.endColor = new Color(1, 1, 1, 0.1f);
lineObject.SetActive(startActive);
return lineObject;
}

Canvas

页面的几个 UI 元素是放在这个下面的。

绘图

交互

脚本位于 Scripts 下面。

经验&问题

如何拆解 Unity 项目

从场景结构开始

Light 的三种模式的区别:Mixed、Realtime、Baked

Hierarchy 中的蓝色立方体是什么?

Prefab(预制件)

比如 Display 这个对象

为什么有的类编辑器无法识别?也没有引入?

比如 Tensor

如何将脚本挂到场景中的对象上?

脚本只要继承自MonoBehaviour,自动就可以进入 Component 池子,然后通过 Editor 中的Add Component就可以加上去了。

脚本中的 public 属性,自动会暴露给 Editor,用户可以自己设置其值。

脚本之间如何通信?

比如 FlattenTexture.cs 中的 srcTexture 属性,是动态变更的,这个是 NetRunnerBehavior.cs 操控其变化的。

1
2
3
public GameObject displayFlatObject;

displayFlatObject.GetComponent<FlattenTexture>().srcTexture = mNetInput[curNetInputIdx];

大致流程:

  • NetRunnerBehavior.cs 暴露 displayFlatObject 这个 GameObject 属性

  • 在 Editor 中将该属性指向场景中的 DisplayFlat 对象

  • 在 DisplayFlat 对象上,挂载 FlattenTexture.cs 脚本

  • 这样 NetRunnerBehavior 中就可以逐层调用,获取和修改 FlattenTexture.srcTexture 属性了

(TODO)Component 系统

可以查看 Add Component 下面支持的组件类型,逐个了解下。

工欲善其事,必先利其器,了解了默认有哪些组件,才能了解 Unity 的能力范围。

事件也是 Component,比如 Event System.

通过 Editor 中的 Component 了解

比如添加一个 Light 组件,就可以看到其支持的属性有哪些。

然后播放过程中,通过动态设置这些属性的值,看看产生的变化,对于属性就有直观的认知了。

参考资料

GitHub - maderix/NetRunner: NetRunner is a simple neural network visualizer created in Unity3D.

At this point it’s a proof of concept highlighting the use of Unity’s inbuilt Baracuda library to load and run neural network models converted from ONNX. This example demonstrates a simple two layer dense network trained in Keras on MNIST dataset and converted to ONNX via Keras2ONNX library [ keras2onnx · PyPI ]

The rendering elements are made using Unity’s HDRP pipeline and free assets.