/// <summary> /// Generates failed checks that correspond to inputs shapes incompatibilities between /// the model and the BrainParameters. /// </summary> /// <param name="model"> /// The Barracuda engine model for loading static parameters /// </param> /// <param name="brainParameters"> /// The BrainParameters that are used verify the compatibility with the InferenceEngine /// </param> /// <param name="sensors">Attached sensors</param> /// <param name="observableAttributeTotalSize">Sum of the sizes of all ObservableAttributes.</param> /// <returns>A IEnumerable of the checks that failed</returns> static IEnumerable <FailedCheck> CheckInputTensorShape( Model model, BrainParameters brainParameters, ISensor[] sensors, int observableAttributeTotalSize) { var failedModelChecks = new List <FailedCheck>(); var tensorTester = new Dictionary <string, Func <BrainParameters, TensorProxy, ISensor[], int, FailedCheck> >() { { TensorNames.PreviousActionPlaceholder, CheckPreviousActionShape }, { TensorNames.RandomNormalEpsilonPlaceholder, ((bp, tensor, scs, i) => null) }, { TensorNames.ActionMaskPlaceholder, ((bp, tensor, scs, i) => null) }, { TensorNames.SequenceLengthPlaceholder, ((bp, tensor, scs, i) => null) }, { TensorNames.RecurrentInPlaceholder, ((bp, tensor, scs, i) => null) }, }; foreach (var mem in model.memories) { tensorTester[mem.input] = ((bp, tensor, scs, i) => null); } for (var sensorIndex = 0; sensorIndex < sensors.Length; sensorIndex++) { var sens = sensors[sensorIndex]; if (sens.GetObservationSpec().Rank == 3) { tensorTester[TensorNames.GetObservationName(sensorIndex)] = (bp, tensor, scs, i) => CheckVisualObsShape(tensor, sens); } if (sens.GetObservationSpec().Rank == 2) { tensorTester[TensorNames.GetObservationName(sensorIndex)] = (bp, tensor, scs, i) => CheckRankTwoObsShape(tensor, sens); } if (sens.GetObservationSpec().Rank == 1) { tensorTester[TensorNames.GetObservationName(sensorIndex)] = (bp, tensor, scs, i) => CheckRankOneObsShape(tensor, sens); } } // If the model expects an input but it is not in this list foreach (var tensor in model.GetInputTensors()) { if (!tensorTester.ContainsKey(tensor.name)) { failedModelChecks.Add(FailedCheck.Warning("Model contains an unexpected input named : " + tensor.name )); } else { var tester = tensorTester[tensor.name]; var error = tester.Invoke(brainParameters, tensor, sensors, observableAttributeTotalSize); if (error != null) { failedModelChecks.Add(error); } } } return(failedModelChecks); }
/// <summary> /// Generates failed checks that correspond to inputs expected by the model that are not /// present in the BrainParameters. /// </summary> /// <param name="model"> /// The Barracuda engine model for loading static parameters /// </param> /// <param name="brainParameters"> /// The BrainParameters that are used verify the compatibility with the InferenceEngine /// </param> /// <param name="memory"> /// The memory size that the model is expecting. /// </param> /// <param name="sensors">Array of attached sensor components</param> /// <returns> /// A IEnumerable of the checks that failed /// </returns> static IEnumerable <FailedCheck> CheckInputTensorPresence( Model model, BrainParameters brainParameters, int memory, ISensor[] sensors ) { var failedModelChecks = new List <FailedCheck>(); var tensorsNames = model.GetInputNames(); for (var sensorIndex = 0; sensorIndex < sensors.Length; sensorIndex++) { if (!tensorsNames.Contains( TensorNames.GetObservationName(sensorIndex))) { var sensor = sensors[sensorIndex]; failedModelChecks.Add( FailedCheck.Warning("The model does not contain an Observation Placeholder Input " + $"for sensor component {sensorIndex} ({sensor.GetType().Name}).") ); } } // If the model has a non-negative memory size but requires a recurrent input if (memory > 0) { var modelVersion = model.GetVersion(); var netHasMemories = false; if (modelVersion < (int)BarracudaModelParamLoader.ModelApiVersion.MLAgents2_0) { netHasMemories = tensorsNames.Any(x => x.EndsWith("_h")) && tensorsNames.Any(x => x.EndsWith("_c")); } else { netHasMemories = tensorsNames.Any(x => x == TensorNames.RecurrentInPlaceholder); } if (!netHasMemories) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain a Recurrent Input Node but has memory_size.") ); } } // If the model uses discrete control but does not have an input for action masks if (model.HasDiscreteOutputs()) { if (!tensorsNames.Contains(TensorNames.ActionMaskPlaceholder)) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain an Action Mask but is using Discrete Control.") ); } } return(failedModelChecks); }
public void InitializeObservations(List <ISensor> sensors, ITensorAllocator allocator) { // Loop through the sensors on a representative agent. // All vector observations use a shared ObservationGenerator since they are concatenated. // All other observations use a unique ObservationInputGenerator var visIndex = 0; ObservationGenerator vecObsGen = null; for (var sensorIndex = 0; sensorIndex < sensors.Count; sensorIndex++) { var sensor = sensors[sensorIndex]; var shape = sensor.GetObservationShape(); var rank = shape.Length; ObservationGenerator obsGen = null; string obsGenName = null; switch (rank) { case 1: if (vecObsGen == null) { vecObsGen = new ObservationGenerator(allocator); } obsGen = vecObsGen; obsGenName = TensorNames.VectorObservationPlaceholder; break; case 2: // If the tensor is of rank 2, we use the index of the sensor // to create the name obsGen = new ObservationGenerator(allocator); obsGenName = TensorNames.GetObservationName(sensorIndex); break; case 3: // If the tensor is of rank 3, we use the "visual observation // index", which only counts the rank 3 sensors obsGen = new ObservationGenerator(allocator); obsGenName = TensorNames.GetVisualObservationName(visIndex); visIndex++; break; default: throw new UnityAgentsException( $"Sensor {sensor.GetName()} have an invalid rank {rank}"); } obsGen.AddSensorIndex(sensorIndex); m_Dict[obsGenName] = obsGen; } }
/// <summary> /// Generates failed checks that correspond to inputs expected by the model that are not /// present in the BrainParameters. Tests the models created with the API of version 1.X /// </summary> /// <param name="model"> /// The Barracuda engine model for loading static parameters /// </param> /// <param name="brainParameters"> /// The BrainParameters that are used verify the compatibility with the InferenceEngine /// </param> /// <param name="memory"> /// The memory size that the model is expecting. /// </param> /// <param name="sensors">Array of attached sensor components</param> /// <returns> /// A IEnumerable of the checks that failed /// </returns> static IEnumerable <FailedCheck> CheckInputTensorPresenceLegacy( Model model, BrainParameters brainParameters, int memory, ISensor[] sensors ) { var failedModelChecks = new List <FailedCheck>(); var tensorsNames = model.GetInputNames(); // If there is no Vector Observation Input but the Brain Parameters expect one. if ((brainParameters.VectorObservationSize != 0) && (!tensorsNames.Contains(TensorNames.VectorObservationPlaceholder))) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain a Vector Observation Placeholder Input. " + "You must set the Vector Observation Space Size to 0.") ); } // If there are not enough Visual Observation Input compared to what the // sensors expect. var visObsIndex = 0; for (var sensorIndex = 0; sensorIndex < sensors.Length; sensorIndex++) { var sensor = sensors[sensorIndex]; if (sensor.GetObservationSpec().Shape.Length == 3) { if (!tensorsNames.Contains( TensorNames.GetVisualObservationName(visObsIndex))) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain a Visual Observation Placeholder Input " + $"for sensor component {visObsIndex} ({sensor.GetType().Name}).") ); } visObsIndex++; } if (sensor.GetObservationSpec().Shape.Length == 2) { if (!tensorsNames.Contains( TensorNames.GetObservationName(sensorIndex))) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain an Observation Placeholder Input " + $"for sensor component {sensorIndex} ({sensor.GetType().Name}).") ); } } } var expectedVisualObs = model.GetNumVisualInputs(); // Check if there's not enough visual sensors (too many would be handled above) if (expectedVisualObs > visObsIndex) { failedModelChecks.Add( FailedCheck.Warning($"The model expects {expectedVisualObs} visual inputs," + $" but only found {visObsIndex} visual sensors.") ); } // If the model has a non-negative memory size but requires a recurrent input if (memory > 0) { if (!tensorsNames.Any(x => x.EndsWith("_h")) || !tensorsNames.Any(x => x.EndsWith("_c"))) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain a Recurrent Input Node but has memory_size.") ); } } // If the model uses discrete control but does not have an input for action masks if (model.HasDiscreteOutputs()) { if (!tensorsNames.Contains(TensorNames.ActionMaskPlaceholder)) { failedModelChecks.Add( FailedCheck.Warning("The model does not contain an Action Mask but is using Discrete Control.") ); } } return(failedModelChecks); }