Files
unity-application/Packages/com.unity.barracuda/Editor/BarracudaAnalytics.cs
2023-03-18 19:53:17 +00:00

149 lines
5.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Onnx;
using UnityEditor;
using UnityEngine.Analytics;
namespace Unity.Barracuda.Editor
{
internal class BarracudaAnalytics
{
static bool s_EventRegistered = false;
const int k_MaxEventsPerHour = 1000;
const int k_MaxNumberOfElements = 1000;
const string k_VendorKey = "unity.barracuda";
const string k_ImportEventName = "uBarracudaImport";
static bool EnableAnalytics()
{
AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_ImportEventName, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey);
if (result == AnalyticsResult.Ok)
s_EventRegistered = true;
return s_EventRegistered;
}
struct BarracudaImportAnalyticsData
{
public string model_type;
public string original_layers;
public string imported_layers;
public string import_warnings;
}
public static void SendBarracudaImportEvent(object originalModel, Model importedModel)
{
//The event shouldn't be able to report if this is disabled but if we know we're not going to report
//Lets early out and not waste time gathering all the data
if (!EditorAnalytics.enabled)
return;
if (!EnableAnalytics())
return;
var data = new BarracudaImportAnalyticsData();
try
{
data.original_layers = AnalyzeONNXModel(originalModel);
data.imported_layers = AnalyzeNNModel(importedModel);
data.model_type = string.IsNullOrEmpty(data.original_layers) ? "NN" : "ONNX";
data.import_warnings = AnalyzeWarnings(importedModel);
}
catch (Exception e)
{
D.LogError($"Failed collecting Barracuda analytics: {e}");
}
EditorAnalytics.SendEventWithLimit(k_ImportEventName, data);
}
static string AnalyzeONNXModel(object originalModel)
{
if (!(originalModel is ModelProto))
return "";
var layers = new Dictionary<string, int>();
var onnxModel = originalModel as ModelProto;
foreach (var node in onnxModel.Graph.Node)
{
var layerDescription = node.OpType;
if (!layers.ContainsKey(layerDescription))
layers[layerDescription] = 1;
else
layers[layerDescription] += 1;
}
return DictionaryToJson(layers);
}
static string AnalyzeNNModel(Model importedModel)
{
var layers = new Dictionary<string, int>();
foreach (Layer layer in importedModel.layers)
{
var layerDescription = LayerToString(layer);
if (!layers.ContainsKey(layerDescription))
layers[layerDescription] = 1;
else
layers[layerDescription] += 1;
}
return DictionaryToJson(layers);
}
static string LayerToString(Layer layer)
{
var layerDescription = layer.type.ToString();
if (layer.type == Layer.Type.Conv2D || layer.type == Layer.Type.Conv2DTrans ||
layer.type == Layer.Type.Conv3D || layer.type == Layer.Type.Conv3DTrans ||
layer.type == Layer.Type.DepthwiseConv2D)
{
layerDescription += "_" + ConvShapeToString(layer);
}
if (layer.activation != Layer.Activation.None)
layerDescription += "_" + layer.activation.ToString();
return layerDescription;
}
static string ConvShapeToString(Layer layer)
{
if (layer.type == Layer.Type.Conv2D ||
layer.type == Layer.Type.DepthwiseConv2D ||
layer.type == Layer.Type.Conv2DTrans)
return string.Join("_",
layer.datasets.Where(d => d.name.EndsWith("/K")).Select(it =>
$"{it.shape.kernelHeight}x{it.shape.kernelWidth}x{it.shape.kernelDepth}x{it.shape.kernelCount}"));
if (layer.type == Layer.Type.Conv3D ||
layer.type == Layer.Type.Conv3DTrans)
return string.Join("_",
layer.datasets.Where(d => d.name.EndsWith("/K")).Select(it =>
$"{it.shape.kernelSpatialDepth}x{it.shape.kernelHeight}x{it.shape.kernelWidth}x{it.shape.kernelDepth}x{it.shape.kernelCount}"));
return "";
}
static string AnalyzeWarnings(Model importedModel)
{
return "[" + string.Join(",",importedModel.Warnings.Select(item => $"'{item.LayerName}:{item.Message}'")) + "]";
}
static string DictionaryToJson(Dictionary<string, int> dict)
{
var entries = dict.Select(d => $"\"{d.Key}\":{string.Join(",", d.Value)}");
return "{" + string.Join(",", entries) + "}";
}
}
}