|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using Unity.Barracuda; |
|
using Unity.MLAgents.Actuators; |
|
using Unity.MLAgents.Inference; |
|
using Unity.MLAgents.Policies; |
|
using Unity.MLAgents.Sensors; |
|
using UnityEngine; |
|
|
|
#if MLA_UNITY_ANALYTICS_MODULE && ENABLE_CLOUD_SERVICES_ANALYTICS |
|
using UnityEngine.Analytics; |
|
#endif |
|
|
|
|
|
#if UNITY_EDITOR |
|
using UnityEditor; |
|
#if MLA_UNITY_ANALYTICS_MODULE |
|
using UnityEditor.Analytics; |
|
#endif // MLA_UNITY_ANALYTICS_MODULE |
|
#endif // UNITY_EDITOR |
|
|
|
|
|
namespace Unity.MLAgents.Analytics |
|
{ |
|
internal class InferenceAnalytics |
|
{ |
|
const string k_VendorKey = "unity.ml-agents"; |
|
const string k_EventName = "ml_agents_inferencemodelset"; |
|
const int k_EventVersion = 1; |
|
|
|
|
|
|
|
|
|
static bool s_EventRegistered; |
|
|
|
|
|
|
|
|
|
const int k_MaxEventsPerHour = 1000; |
|
|
|
|
|
|
|
|
|
const int k_MaxNumberOfElements = 1000; |
|
|
|
|
|
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE && ENABLE_CLOUD_SERVICES_ANALYTICS |
|
|
|
|
|
|
|
private static HashSet<NNModel> s_SentModels; |
|
#endif |
|
|
|
static bool EnableAnalytics() |
|
{ |
|
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE && ENABLE_CLOUD_SERVICES_ANALYTICS |
|
if (s_EventRegistered) |
|
{ |
|
return true; |
|
} |
|
|
|
AnalyticsResult result = EditorAnalytics.RegisterEventWithLimit(k_EventName, k_MaxEventsPerHour, k_MaxNumberOfElements, k_VendorKey, k_EventVersion); |
|
if (result == AnalyticsResult.Ok) |
|
{ |
|
s_EventRegistered = true; |
|
} |
|
if (s_EventRegistered && s_SentModels == null) |
|
{ |
|
s_SentModels = new HashSet<NNModel>(); |
|
} |
|
|
|
#else // no editor, no analytics |
|
s_EventRegistered = false; |
|
#endif |
|
return s_EventRegistered; |
|
} |
|
|
|
public static bool IsAnalyticsEnabled() |
|
{ |
|
#if UNITY_EDITOR |
|
return EditorAnalytics.enabled; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Conditional("MLA_UNITY_ANALYTICS_MODULE")] |
|
public static void InferenceModelSet( |
|
NNModel nnModel, |
|
string behaviorName, |
|
InferenceDevice inferenceDevice, |
|
IList<ISensor> sensors, |
|
ActionSpec actionSpec, |
|
IList<IActuator> actuators |
|
) |
|
{ |
|
#if UNITY_EDITOR && MLA_UNITY_ANALYTICS_MODULE && ENABLE_CLOUD_SERVICES_ANALYTICS |
|
|
|
|
|
if (!IsAnalyticsEnabled()) |
|
return; |
|
|
|
if (!EnableAnalytics()) |
|
return; |
|
|
|
var added = s_SentModels.Add(nnModel); |
|
|
|
if (!added) |
|
{ |
|
|
|
return; |
|
} |
|
|
|
var data = GetEventForModel(nnModel, behaviorName, inferenceDevice, sensors, actionSpec, actuators); |
|
|
|
|
|
if (AnalyticsUtils.s_SendEditorAnalytics) |
|
{ |
|
EditorAnalytics.SendEventWithLimit(k_EventName, data, k_EventVersion); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal static InferenceEvent GetEventForModel( |
|
NNModel nnModel, |
|
string behaviorName, |
|
InferenceDevice inferenceDevice, |
|
IList<ISensor> sensors, |
|
ActionSpec actionSpec, |
|
IList<IActuator> actuators |
|
) |
|
{ |
|
var barracudaModel = ModelLoader.Load(nnModel); |
|
var inferenceEvent = new InferenceEvent(); |
|
|
|
|
|
inferenceEvent.BehaviorName = AnalyticsUtils.Hash(k_VendorKey, behaviorName); |
|
|
|
inferenceEvent.BarracudaModelSource = barracudaModel.IrSource; |
|
inferenceEvent.BarracudaModelVersion = barracudaModel.IrVersion; |
|
inferenceEvent.BarracudaModelProducer = barracudaModel.ProducerName; |
|
inferenceEvent.MemorySize = (int)barracudaModel.GetTensorByName(TensorNames.MemorySize)[0]; |
|
inferenceEvent.InferenceDevice = (int)inferenceDevice; |
|
|
|
if (barracudaModel.ProducerName == "Script") |
|
{ |
|
|
|
inferenceEvent.BarracudaModelSource = "NN"; |
|
inferenceEvent.BarracudaModelProducer = "tensorflow_to_barracuda.py"; |
|
} |
|
|
|
#if UNITY_EDITOR |
|
var barracudaPackageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(Tensor).Assembly); |
|
inferenceEvent.BarracudaPackageVersion = barracudaPackageInfo.version; |
|
#else |
|
inferenceEvent.BarracudaPackageVersion = null; |
|
#endif |
|
|
|
inferenceEvent.ActionSpec = EventActionSpec.FromActionSpec(actionSpec); |
|
inferenceEvent.ObservationSpecs = new List<EventObservationSpec>(sensors.Count); |
|
foreach (var sensor in sensors) |
|
{ |
|
inferenceEvent.ObservationSpecs.Add(EventObservationSpec.FromSensor(sensor)); |
|
} |
|
|
|
inferenceEvent.ActuatorInfos = new List<EventActuatorInfo>(actuators.Count); |
|
foreach (var actuator in actuators) |
|
{ |
|
inferenceEvent.ActuatorInfos.Add(EventActuatorInfo.FromActuator(actuator)); |
|
} |
|
|
|
inferenceEvent.TotalWeightSizeBytes = GetModelWeightSize(barracudaModel); |
|
inferenceEvent.ModelHash = GetModelHash(barracudaModel); |
|
return inferenceEvent; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static long GetModelWeightSize(Model barracudaModel) |
|
{ |
|
long totalWeightsSizeInBytes = 0; |
|
for (var l = 0; l < barracudaModel.layers.Count; ++l) |
|
{ |
|
for (var d = 0; d < barracudaModel.layers[l].datasets.Length; ++d) |
|
{ |
|
totalWeightsSizeInBytes += barracudaModel.layers[l].datasets[d].length; |
|
} |
|
} |
|
return totalWeightsSizeInBytes; |
|
} |
|
|
|
|
|
|
|
|
|
struct MLAgentsHash128 |
|
{ |
|
private Hash128 m_Hash; |
|
|
|
public void Append(float[] values, int count) |
|
{ |
|
if (values == null) |
|
{ |
|
return; |
|
} |
|
|
|
|
|
|
|
#if UNITY_2020_1_OR_NEWER |
|
m_Hash.Append(values, 0, count); |
|
#else |
|
for (var i = 0; i < count; i++) |
|
{ |
|
var tempHash = new Hash128(); |
|
HashUtilities.ComputeHash128(ref values[i], ref tempHash); |
|
HashUtilities.AppendHash(ref tempHash, ref m_Hash); |
|
} |
|
#endif |
|
} |
|
|
|
public void Append(string value) |
|
{ |
|
var tempHash = Hash128.Compute(value); |
|
HashUtilities.AppendHash(ref tempHash, ref m_Hash); |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return m_Hash.ToString(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static string GetModelHash(Model barracudaModel) |
|
{ |
|
var hash = new MLAgentsHash128(); |
|
|
|
|
|
const int kMaxFloats = 256; |
|
|
|
foreach (var layer in barracudaModel.layers) |
|
{ |
|
hash.Append(layer.name); |
|
var numFloatsToHash = Mathf.Min(layer.weights.Length, kMaxFloats); |
|
hash.Append(layer.weights, numFloatsToHash); |
|
} |
|
|
|
return hash.ToString(); |
|
} |
|
} |
|
} |
|
|