浏览代码
New Learning Brain (#1303)
New Learning Brain (#1303)
* Initial Commit * attempt at refactor * Put all static methods into the CoreInternalBrain * improvements * more testing * modifications * renamed epsilon * misc * Now supports discrete actions * added discrete support and RNN and visual. Left to do is refactor and save variables into models * code cleaning * made a tensor generator and applier * fix on the models.py file * Moved the Checks to a different Class * Added some unit tests * BugFix * Need to generate the output tensors as well as inputs before executing the graph * Made NodeNames static and created a new namespace * Added comments to the TensorAppliers * Started adding comments on the TensorGenerators code * Added comments for the Tensor Generator * Moving the helper classes into a separate folder * Added initial comments to the TensorChecks * Renamed NodeNames -> TensorNames * Removing warnings in tests * Now using Aut.../develop-generalizationTraining-TrainerController
GitHub
6 年前
当前提交
6c354d16
共有 31 个文件被更改,包括 1626 次插入 和 458 次删除
-
56UnitySDK/Assets/ML-Agents/Editor/LearningBrainEditor.cs
-
1UnitySDK/Assets/ML-Agents/Editor/Tests/MLAgentsEditModeTest.cs
-
3UnitySDK/Assets/ML-Agents/Editor/Tests/UtilitiesTests.cs
-
20UnitySDK/Assets/ML-Agents/Scripts/Brain.cs
-
1UnitySDK/Assets/ML-Agents/Scripts/HeuristicBrain.cs
-
498UnitySDK/Assets/ML-Agents/Scripts/LearningBrain.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/LearningBrain.cs.meta
-
2UnitySDK/Assets/ML-Agents/Scripts/PlayerBrain.cs
-
6ml-agents/mlagents/trainers/bc/models.py
-
27ml-agents/mlagents/trainers/models.py
-
8ml-agents/mlagents/trainers/policy.py
-
10ml-agents/mlagents/trainers/ppo/policy.py
-
3ml-agents/mlagents/trainers/ppo/trainer.py
-
138UnitySDK/Assets/ML-Agents/Editor/Tests/EditModeTestInternalBrainTensorApplier.cs
-
3UnitySDK/Assets/ML-Agents/Editor/Tests/EditModeTestInternalBrainTensorApplier.cs.meta
-
158UnitySDK/Assets/ML-Agents/Editor/Tests/EditModeTestInternalBrainTensorGenerator.cs
-
11UnitySDK/Assets/ML-Agents/Editor/Tests/EditModeTestInternalBrainTensorGenerator.cs.meta
-
8UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain.meta
-
143UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/ApplierImpl.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/ApplierImpl.cs.meta
-
224UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/GeneratorImpl.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/GeneratorImpl.cs.meta
-
531UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/ModelParamLoader.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/ModelParamLoader.cs.meta
-
80UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorApplier.cs
-
11UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorApplier.cs.meta
-
99UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorGenerator.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorGenerator.cs.meta
-
25UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorNames.cs
-
3UnitySDK/Assets/ML-Agents/Scripts/InferenceBrain/TensorNames.cs.meta
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using NUnit.Framework; |
|||
using UnityEngine; |
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
using System.Reflection; |
|||
using MLAgents.InferenceBrain; |
|||
|
|||
namespace MLAgents.Tests |
|||
{ |
|||
public class EditModeTestInternalBrainTensorApplier |
|||
{ |
|||
private class TestAgent : Agent |
|||
{ |
|||
public AgentAction GetAction() |
|||
{ |
|||
FieldInfo f = typeof(Agent).GetField( |
|||
"action", BindingFlags.Instance | BindingFlags.NonPublic); |
|||
return (AgentAction) f.GetValue(this); |
|||
} |
|||
} |
|||
|
|||
private Dictionary<Agent, AgentInfo> GetFakeAgentInfos() |
|||
{ |
|||
var goA = new GameObject("goA"); |
|||
var agentA = goA.AddComponent<TestAgent>(); |
|||
var infoA = new AgentInfo(); |
|||
var goB = new GameObject("goB"); |
|||
var agentB = goB.AddComponent<TestAgent>(); |
|||
var infoB = new AgentInfo(); |
|||
|
|||
return new Dictionary<Agent, AgentInfo>(){{agentA, infoA},{agentB, infoB}}; |
|||
} |
|||
|
|||
[Test] |
|||
public void Contruction() |
|||
{ |
|||
var bp = new BrainParameters(); |
|||
var tensorGenerator = new TensorApplier(bp, 0); |
|||
Assert.IsNotNull(tensorGenerator); |
|||
} |
|||
|
|||
[Test] |
|||
public void ApplyContinuousActionOutput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 3}, |
|||
Data = new float[,] {{1, 2, 3}, {4, 5, 6}} |
|||
}; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var applier = new ContinuousActionOutputApplier(); |
|||
applier.Apply(inputTensor, agentInfos); |
|||
var agents = agentInfos.Keys.ToList(); |
|||
var agent = agents[0] as TestAgent; |
|||
var action = agent.GetAction(); |
|||
Assert.AreEqual(action.vectorActions[0], 1); |
|||
Assert.AreEqual(action.vectorActions[1], 2); |
|||
Assert.AreEqual(action.vectorActions[2], 3); |
|||
agent = agents[1] as TestAgent; |
|||
action = agent.GetAction(); |
|||
Assert.AreEqual(action.vectorActions[0], 4); |
|||
Assert.AreEqual(action.vectorActions[1], 5); |
|||
Assert.AreEqual(action.vectorActions[2], 6); |
|||
} |
|||
|
|||
[Test] |
|||
public void ApplyDiscreteActionOutput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 5}, |
|||
Data = new float[,] {{0.5f, 22.5f, 0.1f, 5f, 1f}, |
|||
{4f, 5f, 6f, 7f, 8f}} |
|||
}; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var applier = new DiscreteActionOutputApplier(new int[]{2, 3}, 0); |
|||
applier.Apply(inputTensor, agentInfos); |
|||
var agents = agentInfos.Keys.ToList(); |
|||
var agent = agents[0] as TestAgent; |
|||
var action = agent.GetAction(); |
|||
Assert.AreEqual(action.vectorActions[0], 1); |
|||
Assert.AreEqual(action.vectorActions[1], 1); |
|||
agent = agents[1] as TestAgent; |
|||
action = agent.GetAction(); |
|||
Assert.AreEqual(action.vectorActions[0], 1); |
|||
Assert.AreEqual(action.vectorActions[1], 2); |
|||
} |
|||
|
|||
[Test] |
|||
public void ApplyMemoryOutput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 5}, |
|||
Data = new float[,] {{0.5f, 22.5f, 0.1f, 5f, 1f}, |
|||
{4f, 5f, 6f, 7f, 8f}} |
|||
}; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var applier = new MemoryOutputApplier(); |
|||
applier.Apply(inputTensor, agentInfos); |
|||
var agents = agentInfos.Keys.ToList(); |
|||
var agent = agents[0] as TestAgent; |
|||
var action = agent.GetAction(); |
|||
Assert.AreEqual(action.memories[0], 0.5f); |
|||
Assert.AreEqual(action.memories[1], 22.5f); |
|||
agent = agents[1] as TestAgent; |
|||
action = agent.GetAction(); |
|||
Assert.AreEqual(action.memories[2], 6); |
|||
Assert.AreEqual(action.memories[3], 7); |
|||
} |
|||
|
|||
[Test] |
|||
public void ApplyValueEstimate() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 1}, |
|||
Data = new float[,] {{0.5f}, {8f}} |
|||
}; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var applier = new ValueEstimateApplier(); |
|||
applier.Apply(inputTensor, agentInfos); |
|||
var agents = agentInfos.Keys.ToList(); |
|||
var agent = agents[0] as TestAgent; |
|||
var action = agent.GetAction(); |
|||
Assert.AreEqual(action.value, 0.5f); |
|||
agent = agents[1] as TestAgent; |
|||
action = agent.GetAction(); |
|||
Assert.AreEqual(action.value, 8); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: be419f7ed5c24b24a6f2636d3b107535 |
|||
timeCreated: 1537915674 |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using NUnit.Framework; |
|||
using UnityEngine; |
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
using MLAgents.InferenceBrain; |
|||
|
|||
namespace MLAgents.Tests |
|||
{ |
|||
public class EditModeTestInternalBrainTensorGenerator |
|||
{ |
|||
private class TestAgent : Agent |
|||
{ |
|||
|
|||
} |
|||
|
|||
private Dictionary<Agent, AgentInfo> GetFakeAgentInfos() |
|||
{ |
|||
var goA = new GameObject("goA"); |
|||
var agentA = goA.AddComponent<TestAgent>(); |
|||
var infoA = new AgentInfo() |
|||
{ |
|||
stackedVectorObservation = (new float[] {1f, 2f, 3f}).ToList(), |
|||
memories = null, |
|||
storedVectorActions = new float[] {1, 2}, |
|||
actionMasks = null, |
|||
|
|||
}; |
|||
var goB = new GameObject("goB"); |
|||
var agentB = goB.AddComponent<TestAgent>(); |
|||
var infoB = new AgentInfo() |
|||
{ |
|||
stackedVectorObservation = (new float[] {4f, 5f, 6f}).ToList(), |
|||
memories = (new float[] {1f, 1f, 1f}).ToList(), |
|||
storedVectorActions = new float[] {3, 4}, |
|||
actionMasks = new bool[] {true, false, false, false, false}, |
|||
}; |
|||
|
|||
return new Dictionary<Agent, AgentInfo>(){{agentA, infoA},{agentB, infoB}}; |
|||
} |
|||
|
|||
[Test] |
|||
public void Contruction() |
|||
{ |
|||
var bp = new BrainParameters(); |
|||
var tensorGenerator = new TensorGenerator(bp, 0); |
|||
Assert.IsNotNull(tensorGenerator); |
|||
} |
|||
|
|||
[Test] |
|||
public void GenerateBatchSize() |
|||
{ |
|||
var inputTensor = new Tensor(); |
|||
var batchSize = 4; |
|||
var generator = new BatchSizeGenerator(); |
|||
generator.Generate(inputTensor, batchSize, null); |
|||
Assert.IsNotNull(inputTensor.Data as int[]); |
|||
Assert.AreEqual((inputTensor.Data as int[])[0], batchSize); |
|||
} |
|||
|
|||
[Test] |
|||
public void GenerateSequenceLength() |
|||
{ |
|||
var inputTensor = new Tensor(); |
|||
var batchSize = 4; |
|||
var generator = new SequenceLengthGenerator(); |
|||
generator.Generate(inputTensor, batchSize, null); |
|||
Assert.IsNotNull(inputTensor.Data as int[]); |
|||
Assert.AreEqual((inputTensor.Data as int[])[0], 1); |
|||
} |
|||
|
|||
[Test] |
|||
public void GenerateVectorObservation() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 3} |
|||
}; |
|||
var batchSize = 4; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var generator = new VectorObservationGenerator(); |
|||
generator.Generate(inputTensor, batchSize, agentInfos); |
|||
Assert.IsNotNull(inputTensor.Data as float[,]); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 0], 1); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 2], 3); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 0], 4); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 2], 6); |
|||
} |
|||
|
|||
[Test] |
|||
public void GenerateRecurrentInput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 5} |
|||
}; |
|||
var batchSize = 4; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var generator = new RecurrentInputGenerator(); |
|||
generator.Generate(inputTensor, batchSize, agentInfos); |
|||
Assert.IsNotNull(inputTensor.Data as float[,]); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 0], 0); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 4], 0); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 0], 1); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 4], 0); |
|||
} |
|||
|
|||
[Test] |
|||
public void GeneratePreviousActionInput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 2}, |
|||
ValueType = Tensor.TensorType.FloatingPoint |
|||
|
|||
}; |
|||
var batchSize = 4; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var generator = new PreviousActionInputGenerator(); |
|||
Assert.Catch<NotImplementedException>( |
|||
() => generator.Generate(inputTensor, batchSize, agentInfos)); |
|||
|
|||
inputTensor.ValueType = Tensor.TensorType.Integer; |
|||
generator.Generate(inputTensor, batchSize, agentInfos); |
|||
Assert.IsNotNull(inputTensor.Data as int[,]); |
|||
Assert.AreEqual((inputTensor.Data as int[,])[0, 0], 1); |
|||
Assert.AreEqual((inputTensor.Data as int[,])[0, 1], 2); |
|||
Assert.AreEqual((inputTensor.Data as int[,])[1, 0], 3); |
|||
Assert.AreEqual((inputTensor.Data as int[,])[1, 1], 4); |
|||
} |
|||
|
|||
[Test] |
|||
public void GenerateActionMaskInput() |
|||
{ |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
Shape = new long[] {2, 5}, |
|||
ValueType = Tensor.TensorType.FloatingPoint |
|||
|
|||
}; |
|||
var batchSize = 4; |
|||
var agentInfos = GetFakeAgentInfos(); |
|||
|
|||
var generator = new ActionMaskInputGenerator(); |
|||
generator.Generate(inputTensor, batchSize, agentInfos); |
|||
Assert.IsNotNull(inputTensor.Data as float[,]); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 0], 1); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[0, 4], 1); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 0], 0); |
|||
Assert.AreEqual((inputTensor.Data as float[,])[1, 4], 1); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d2d2076c51c414ac7a91f8fbf15d4f7c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 79c170c0af66140e68d7eca827f0d788 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// The Applier for the Continuous Action output tensor. Tensor is assumed to contain the
|
|||
/// continuous action data of the agents in the batch.
|
|||
/// </summary>
|
|||
public class ContinuousActionOutputApplier : TensorApplier.Applier |
|||
{ |
|||
public void Apply(Tensor tensor, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var tensorDataAction = tensor.Data as float[,]; |
|||
var actionSize = tensor.Shape[1]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var action = new float[actionSize]; |
|||
for (var j = 0; j < actionSize; j++) |
|||
{ |
|||
action[j] = tensorDataAction[agentIndex, j]; |
|||
} |
|||
agent.UpdateVectorAction(action); |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The Applier for the Discrete Action output tensor. Uses multinomial to sample discrete
|
|||
/// actions from the logits contained in the tensor.
|
|||
/// </summary>
|
|||
public class DiscreteActionOutputApplier : TensorApplier.Applier |
|||
{ |
|||
private int[] _actionSize; |
|||
private Multinomial _multinomial; |
|||
|
|||
public DiscreteActionOutputApplier(int[] actionSize, int seed) |
|||
{ |
|||
_actionSize = actionSize; |
|||
_multinomial = new Multinomial(seed); |
|||
} |
|||
|
|||
public void Apply(Tensor tensor, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var tensorDataProbabilities = tensor.Data as float[,]; |
|||
var batchSize = agentInfo.Keys.Count; |
|||
var actions = new float[batchSize, _actionSize.Length]; |
|||
var startActionIndices = Utilities.CumSum(_actionSize); |
|||
for (var actionIndex=0; actionIndex < _actionSize.Length; actionIndex++) |
|||
{ |
|||
var nBranchAction = _actionSize[actionIndex]; |
|||
var actionProbs = new float[batchSize, nBranchAction]; |
|||
for (var batchIndex = 0; batchIndex < batchSize; batchIndex++) |
|||
{ |
|||
for (var branchActionIndex = 0; |
|||
branchActionIndex < nBranchAction; |
|||
branchActionIndex++) |
|||
{ |
|||
actionProbs[batchIndex, branchActionIndex] = |
|||
tensorDataProbabilities[ |
|||
batchIndex, startActionIndices[actionIndex] + branchActionIndex]; |
|||
} |
|||
} |
|||
var inputTensor = new Tensor() |
|||
{ |
|||
ValueType = Tensor.TensorType.FloatingPoint, |
|||
Shape = new long[]{batchSize, _actionSize[actionIndex]}, |
|||
Data = actionProbs |
|||
}; |
|||
var outputTensor = new Tensor() |
|||
{ |
|||
ValueType = Tensor.TensorType.FloatingPoint, |
|||
Shape = new long[]{batchSize, 1}, |
|||
Data = new float[batchSize, 1] |
|||
}; |
|||
_multinomial.Eval(inputTensor, outputTensor); |
|||
var outTensor = outputTensor.Data as float[,]; |
|||
for (var ii = 0; ii < batchSize; ii++) |
|||
{ |
|||
actions[ii, actionIndex] = outTensor[ii, 0]; |
|||
} |
|||
} |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var action = new float[_actionSize.Length]; |
|||
for (var j = 0; j < _actionSize.Length; j++) |
|||
{ |
|||
action[j] = actions[agentIndex, j]; |
|||
} |
|||
agent.UpdateVectorAction(action); |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The Applier for the Memory output tensor. Tensor is assumed to contain the new
|
|||
/// memory data of the agents in the batch.
|
|||
/// </summary>
|
|||
public class MemoryOutputApplier : TensorApplier.Applier |
|||
{ |
|||
public void Apply(Tensor tensor, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var tensorDataMemory = tensor.Data as float[,]; |
|||
var agentIndex = 0; |
|||
var memorySize = tensor.Shape[1]; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var memory = new List<float>(); |
|||
for (var j = 0; j < memorySize; j++) |
|||
{ |
|||
memory.Add(tensorDataMemory[agentIndex, j]); |
|||
} |
|||
|
|||
agent.UpdateMemoriesAction(memory); |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The Applier for the Value Estimate output tensor. Tensor is assumed to contain the
|
|||
/// value estimates of the agents in the batch.
|
|||
/// </summary>
|
|||
public class ValueEstimateApplier : TensorApplier.Applier |
|||
{ |
|||
public void Apply(Tensor tensor, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var tensorDataValue = tensor.Data as float[,]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
agent.UpdateValueAction(tensorDataValue[agentIndex, 0]); |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 99d5dc2d52e442d1a1f466a246cfb28d |
|||
timeCreated: 1539118675 |
|
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using System.Collections.Generic; |
|||
using System; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
using System.Linq; |
|||
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// Reshapes a Tensor so that its first dimension becomes equal to the current batch size
|
|||
/// and initializes its content to be zeros. Will only work on 2-dimensional tensors.
|
|||
/// The second dimension of the Tensor will not be modified.
|
|||
/// </summary>
|
|||
public class BiDimensionalOutputGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var shapeSecondAxis = tensor.Shape[1]; |
|||
tensor.Shape[0] = batchSize; |
|||
if (tensor.ValueType == Tensor.TensorType.FloatingPoint) |
|||
{ |
|||
tensor.Data = new float[batchSize, shapeSecondAxis]; |
|||
} |
|||
else |
|||
{ |
|||
tensor.Data = new int[batchSize, shapeSecondAxis]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the BatchSize input : Will be a one dimensional
|
|||
/// integer array of size 1 containing the batch size.
|
|||
/// </summary>
|
|||
public class BatchSizeGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Data = new int[] {batchSize}; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the SequenceLength input : Will be a one
|
|||
/// dimensional integer array of size 1 containing 1.
|
|||
/// Note : the sequence length is always one since recurrent networks only predict for
|
|||
/// one step at the time.
|
|||
/// </summary>
|
|||
public class SequenceLengthGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Data = new int[] {1}; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the VectorObservation input : Will be a two
|
|||
/// dimensional float array of dimension [batchSize x vectorObservationSize].
|
|||
/// It will use the Vector Observation data contained in the agentInfo to fill the data
|
|||
/// of the tensor.
|
|||
/// </summary>
|
|||
public class VectorObservationGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Shape[0] = batchSize; |
|||
var vecObsSizeT = tensor.Shape[1]; |
|||
tensor.Data = new float[batchSize, vecObsSizeT]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var vectorObs = agentInfo[agent].stackedVectorObservation; |
|||
for (var j = 0; j < vecObsSizeT; j++) |
|||
{ |
|||
tensor.Data.SetValue(vectorObs[j], new int[2] {agentIndex, j}); |
|||
} |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the Recurrent input : Will be a two
|
|||
/// dimensional float array of dimension [batchSize x memorySize].
|
|||
/// It will use the Memory data contained in the agentInfo to fill the data
|
|||
/// of the tensor.
|
|||
/// </summary>
|
|||
public class RecurrentInputGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Shape[0] = batchSize; |
|||
var memorySize = tensor.Shape[1]; |
|||
tensor.Data = new float[batchSize, memorySize]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var memory = agentInfo[agent].memories; |
|||
if (memory == null) |
|||
{ |
|||
agentIndex++; |
|||
continue; |
|||
} |
|||
for (var j = 0; j < Math.Min(memorySize, memory.Count); j++) |
|||
{ |
|||
if (j >= memory.Count) |
|||
{ |
|||
break; |
|||
} |
|||
tensor.Data.SetValue(memory[j], new int[2] {agentIndex, j}); |
|||
} |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the Previous Action input : Will be a two
|
|||
/// dimensional integer array of dimension [batchSize x actionSize].
|
|||
/// It will use the previous action data contained in the agentInfo to fill the data
|
|||
/// of the tensor.
|
|||
/// </summary>
|
|||
public class PreviousActionInputGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
if (tensor.ValueType != Tensor.TensorType.Integer) |
|||
{ |
|||
throw new NotImplementedException( |
|||
"Previous Action Inputs are only valid for discrete control"); |
|||
} |
|||
|
|||
tensor.Shape[0] = batchSize; |
|||
var actionSize = tensor.Shape[1]; |
|||
tensor.Data = new int[batchSize, actionSize]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var pastAction = agentInfo[agent].storedVectorActions; |
|||
for (var j = 0; j < actionSize; j++) |
|||
{ |
|||
tensor.Data.SetValue((int) pastAction[j], new int[2] {agentIndex, j}); |
|||
} |
|||
|
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the Action Mask input : Will be a two
|
|||
/// dimensional float array of dimension [batchSize x numActionLogits].
|
|||
/// It will use the Action Mask data contained in the agentInfo to fill the data
|
|||
/// of the tensor.
|
|||
/// </summary>
|
|||
public class ActionMaskInputGenerator : TensorGenerator.Generator |
|||
{ |
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Shape[0] = batchSize; |
|||
var maskSize = tensor.Shape[1]; |
|||
tensor.Data = new float[batchSize, maskSize]; |
|||
var agentIndex = 0; |
|||
foreach (var agent in agentInfo.Keys) |
|||
{ |
|||
var maskList = agentInfo[agent].actionMasks; |
|||
for (var j = 0; j < maskSize; j++) |
|||
{ |
|||
var isUnmasked = (maskList != null && maskList[j]) ? 0.0f : 1.0f; |
|||
tensor.Data.SetValue(isUnmasked, new int[2] {agentIndex, j}); |
|||
} |
|||
agentIndex++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the Epsilon input : Will be a two
|
|||
/// dimensional float array of dimension [batchSize x actionSize].
|
|||
/// It will use the generate random input data from a normal Distribution.
|
|||
/// </summary>
|
|||
public class RandomNormalInputGenerator : TensorGenerator.Generator |
|||
{ |
|||
private RandomNormal _randomNormal; |
|||
|
|||
public RandomNormalInputGenerator(int seed) |
|||
{ |
|||
_randomNormal = new RandomNormal(seed); |
|||
} |
|||
|
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
tensor.Shape[0] = batchSize; |
|||
var actionSize = tensor.Shape[1]; |
|||
tensor.Data = new float[batchSize, actionSize]; |
|||
_randomNormal.FillTensor(tensor); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor corresponding to the Visual Observation input : Will be a 4
|
|||
/// dimensional float array of dimension [batchSize x width x heigth x numChannels].
|
|||
/// It will use the Texture input data contained in the agentInfo to fill the data
|
|||
/// of the tensor.
|
|||
/// </summary>
|
|||
public class VisualObservationInputGenerator : TensorGenerator.Generator |
|||
{ |
|||
private int _index; |
|||
private bool _grayScale; |
|||
public VisualObservationInputGenerator(int index, bool grayScale) |
|||
{ |
|||
_index = index; |
|||
_grayScale = grayScale; |
|||
} |
|||
|
|||
public void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo) |
|||
{ |
|||
var textures = agentInfo.Keys.Select( |
|||
agent => agentInfo[agent].visualObservations[_index]).ToList(); |
|||
tensor.Data = Utilities.TextureToFloatArray(textures, _grayScale); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c57a4989c7e54b93ab56293698d7d237 |
|||
timeCreated: 1539109542 |
|
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// Prepares the Tensors for the Learning Brain and exposes a list of failed checks if Model
|
|||
/// and BrainParameters are incompatible.
|
|||
/// </summary>
|
|||
public class ModelParamLoader |
|||
{ |
|||
private enum ModelActionType |
|||
{ |
|||
Unknown, |
|||
Discrete, |
|||
Continuous |
|||
} |
|||
private const long ApiVersion = 1; |
|||
private InferenceEngine _engine; |
|||
private BrainParameters _brainParameters; |
|||
private List<string> _failedModelChecks = new List<string>(); |
|||
|
|||
/// <summary>
|
|||
/// Factory for the ModelParamLoader : Creates a ModelParamLoader and runs the checks
|
|||
/// on it.
|
|||
/// </summary>
|
|||
/// <param name="engine"> The InferenceEngine we get the parameters and the checks from
|
|||
/// </param>
|
|||
/// <param name="brainParameters"> The BrainParamters that are used verify the
|
|||
/// compatibility with the InferenceEngine</param>
|
|||
/// <returns></returns>
|
|||
public static ModelParamLoader GetLoaderAndCheck(InferenceEngine engine, |
|||
BrainParameters brainParameters) |
|||
{ |
|||
ModelParamLoader modelParamLoader = new ModelParamLoader(engine, brainParameters); |
|||
modelParamLoader.GenerateChecks(); |
|||
return modelParamLoader; |
|||
} |
|||
|
|||
private ModelParamLoader(InferenceEngine engine, BrainParameters brainParameters) |
|||
{ |
|||
_engine = engine; |
|||
_brainParameters = brainParameters; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor inputs that are expected to be present in the Model.
|
|||
/// </summary>
|
|||
/// <returns>Tensor IEnumerable with the expected Tensor inputs</returns>
|
|||
public IEnumerable<Tensor> GetInputTensors() |
|||
{ |
|||
return _engine?.InputFeatures(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the Tensor outputs that are expected to be present in the Model.
|
|||
/// </summary>
|
|||
/// <returns>Tensor IEnumerable with the expected Tensor outputs</returns>
|
|||
public IEnumerable<Tensor> GetOutputTensors() |
|||
{ |
|||
var tensorList = new List<Tensor>(); |
|||
if (_brainParameters.vectorActionSpaceType == SpaceType.continuous) |
|||
{ |
|||
tensorList.Add(new Tensor() |
|||
{ |
|||
Name = TensorNames.ActionOutput, |
|||
Shape = new long[] |
|||
{ |
|||
-1, _brainParameters.vectorActionSize[0] |
|||
}, |
|||
ValueType = Tensor.TensorType.FloatingPoint, |
|||
Data = null |
|||
}); |
|||
} |
|||
else |
|||
{ |
|||
tensorList.Add( |
|||
new Tensor() |
|||
{ |
|||
Name = TensorNames.ActionOutput, |
|||
Shape = new long[] |
|||
{ |
|||
-1, _brainParameters.vectorActionSize.Sum() |
|||
}, |
|||
ValueType = Tensor.TensorType.FloatingPoint, |
|||
Data = null |
|||
}); |
|||
} |
|||
var memory = GetIntScalar(TensorNames.MemorySize); |
|||
if (memory > 0) |
|||
{ |
|||
tensorList.Add(new Tensor() |
|||
{ |
|||
Name = TensorNames.RecurrentOutput, |
|||
Shape = new long[2] |
|||
{ |
|||
-1, memory |
|||
}, |
|||
ValueType = Tensor.TensorType.FloatingPoint, |
|||
Data = null |
|||
}); |
|||
} |
|||
return tensorList; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Queries the InferenceEngine for the value of a variable in the graph given its name.
|
|||
/// Only works with int32 Tensors with zero dimensions containing a unique element.
|
|||
/// If the node was not found or could not be retrieved, the value -1 will be returned.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the Tensor variable</param>
|
|||
/// <returns>The value of the scalar variable in the model. (-1 if not found)</returns>
|
|||
private int GetIntScalar(string name) |
|||
{ |
|||
var outputs = new Tensor[] |
|||
{ |
|||
new Tensor() |
|||
{ |
|||
Name = name, |
|||
ValueType = Tensor.TensorType.Integer, |
|||
Shape = new long[] { }, |
|||
Data = new long[1] |
|||
}, |
|||
}; |
|||
try |
|||
{ |
|||
_engine.ExecuteGraph(new Tensor[0], outputs); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Debug.Log("Node not in graph: " + name + ". The following error occured : \n" + e); |
|||
return -1; |
|||
} |
|||
return (outputs[0].Data as int[])[0]; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves an IEnumerable of string corresponding to the failed compatibility checks
|
|||
/// between the InferenceEngine and the BrainParameters.
|
|||
/// </summary>
|
|||
public IEnumerable<string> GetChecks() |
|||
{ |
|||
return _failedModelChecks; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates the list of failed checks that failed when comparing the data from the Model
|
|||
/// and from the BrainParameters
|
|||
/// </summary>
|
|||
private void GenerateChecks() |
|||
{ |
|||
_failedModelChecks.Clear(); |
|||
if (_engine == null) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"There is no model for this Brain, cannot run inference. " + |
|||
"(But can still train)"); |
|||
return; |
|||
} |
|||
|
|||
var modelApiVersion = GetIntScalar(TensorNames.VersionNumber); |
|||
var memorySize = GetIntScalar(TensorNames.MemorySize); |
|||
var isContinuousInt = GetIntScalar(TensorNames.IsContinuousControl); |
|||
var isContinuous = GetActionType(isContinuousInt); |
|||
var actionSize = GetIntScalar(TensorNames.ActionOutputShape); |
|||
if (modelApiVersion == -1) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"Model was not trained using the right version of ML-Agents. Cannot use this " + |
|||
"model."); |
|||
return; |
|||
} |
|||
if (modelApiVersion != ApiVersion) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
$"Version of the trainer the model was trained with ({modelApiVersion}) " + |
|||
$"is not compatible with the Brain's version ({ApiVersion})."); |
|||
return; |
|||
} |
|||
|
|||
CheckIntScalarPresenceHelper(new Dictionary<string, int>() |
|||
{ |
|||
{TensorNames.MemorySize, memorySize}, |
|||
{TensorNames.IsContinuousControl, isContinuousInt}, |
|||
{TensorNames.ActionOutputShape, actionSize} |
|||
}); |
|||
CheckInputTensorPresence(memorySize, isContinuous); |
|||
CheckOutputTensorPresence(memorySize); |
|||
CheckInputTensorShape(); |
|||
CheckOutputTensorShape(isContinuous, actionSize); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Converts the integer value in the model corresponding to the type of control to a
|
|||
/// ModelActionType.
|
|||
/// </summary>
|
|||
/// <param name="isContinuousInt"> The integer value in the model indicating the
|
|||
/// type of control</param>
|
|||
/// <returns>The equivalent ModelActionType</returns>
|
|||
private static ModelActionType GetActionType(int isContinuousInt) |
|||
{ |
|||
ModelActionType isContinuous; |
|||
switch (isContinuousInt) |
|||
{ |
|||
case 0: |
|||
isContinuous = ModelActionType.Discrete; |
|||
break; |
|||
case 1: |
|||
isContinuous = ModelActionType.Continuous; |
|||
break; |
|||
default: |
|||
isContinuous = ModelActionType.Unknown; |
|||
break; |
|||
} |
|||
return isContinuous; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Given a Dictionary of node names to int values, create checks if the values have the
|
|||
/// invalid value of -1.
|
|||
/// </summary>
|
|||
/// <param name="requiredScalarFields"> Mapping from node names to int values</param>
|
|||
private void CheckIntScalarPresenceHelper(Dictionary<string, int> requiredScalarFields) |
|||
{ |
|||
foreach(var field in requiredScalarFields) |
|||
if (field.Value == -1) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
$"Missing node in the model provided : {field.Key}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates failed checks that correspond to inputs expected by the model that are not
|
|||
/// present in the BrainParameters.
|
|||
/// </summary>
|
|||
/// <param name="memory"> The memory size that the model is expecting/</param>
|
|||
/// <param name="isContinuous"> Whether the model is expecting continuous or
|
|||
/// discrete control.</param>
|
|||
/// <returns>A IEnumerable of string corresponding to the failed input presence
|
|||
/// checks.</returns>
|
|||
private void CheckInputTensorPresence(int memory, ModelActionType isContinuous) |
|||
{ |
|||
var tensorsNames = GetInputTensors().Select(x => x.Name).ToList(); |
|||
// If there is no Vector Observation Input but the Brain Parameters expect one.
|
|||
if ((_brainParameters.vectorObservationSize != 0) && |
|||
(!tensorsNames.Contains(TensorNames.VectorObservationPlacholder))) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"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
|
|||
// Brain Parameters expect.
|
|||
for (var visObsIndex = 0; |
|||
visObsIndex < _brainParameters.cameraResolutions.Length; |
|||
visObsIndex++) |
|||
{ |
|||
if (!tensorsNames.Contains( |
|||
TensorNames.VisualObservationPlaceholderPrefix + visObsIndex)) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"The model does not contain a Visual Observation Placeholder Input " + |
|||
"for visual observation "+visObsIndex+"."); |
|||
} |
|||
} |
|||
// If the model has a non-negative memory size but requires a recurrent input
|
|||
if (memory > 0) |
|||
{ |
|||
if (!tensorsNames.Contains(TensorNames.RecurrentInPlaceholder)) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"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 (isContinuous == ModelActionType.Discrete) |
|||
{ |
|||
if (!tensorsNames.Contains(TensorNames.ActionMaskPlaceholder)) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"The model does not contain an Action Mask but is using Discrete Control."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates failed checks that correspond to outputs expected by the model that are not
|
|||
/// present in the BrainParameters.
|
|||
/// </summary>
|
|||
/// <param name="memory"> The memory size that the model is expecting/</param>
|
|||
/// <returns>A IEnumerable of string corresponding to the failed output presence
|
|||
/// checks.</returns>
|
|||
private void CheckOutputTensorPresence(int memory) |
|||
{ |
|||
var tensorsNames = GetOutputTensors().Select(x => x.Name).ToList(); |
|||
// If there is no Action Output.
|
|||
if (!tensorsNames.Contains(TensorNames.ActionOutput)) |
|||
{ |
|||
_failedModelChecks.Add("The model does not contain an Action Output Node."); |
|||
} |
|||
|
|||
// If there is no Recurrent Output but the model is Recurrent.
|
|||
if (memory > 0) |
|||
{ |
|||
if (!tensorsNames.Contains(TensorNames.RecurrentOutput)) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"The model does not contain a Recurrent Output Node but has memory_size."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates failed checks that correspond to inputs shapes incompatibilities between
|
|||
/// the model and the BrainParameters.
|
|||
/// </summary>
|
|||
private void CheckInputTensorShape() |
|||
{ |
|||
var tensorTester = |
|||
new Dictionary<string, Func<Tensor, string>>() |
|||
{ |
|||
{TensorNames.VectorObservationPlacholder, CheckVectorObsShape}, |
|||
{TensorNames.PreviousActionPlaceholder, CheckPreviousActionShape}, |
|||
{TensorNames.RandomNormalEpsilonPlaceholder, ((tensor) => null)}, |
|||
{TensorNames.ActionMaskPlaceholder, ((tensor) => null)}, |
|||
{TensorNames.SequenceLengthPlaceholder, ((tensor) => null)}, |
|||
{TensorNames.RecurrentInPlaceholder, ((tensor) => null)}, |
|||
}; |
|||
for (var obsIndex = 0; obsIndex < _brainParameters.cameraResolutions.Length; obsIndex++) |
|||
{ |
|||
var index = obsIndex; |
|||
tensorTester[TensorNames.VisualObservationPlaceholderPrefix + obsIndex] = |
|||
(tensor) => CheckVisualObsShape(tensor, index); |
|||
} |
|||
// If the model expects an input but it is not in this list
|
|||
foreach (var tensor in GetInputTensors()) |
|||
{ |
|||
if (!tensorTester.ContainsKey(tensor.Name)) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"Model requires an unknown input named : " + tensor.Name); |
|||
} |
|||
else |
|||
{ |
|||
var tester = tensorTester[tensor.Name]; |
|||
var error = tester.Invoke(tensor); |
|||
if (error != null) |
|||
{ |
|||
_failedModelChecks.Add(error); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks that the shape of the Vector Observation input placeholder is the same in the
|
|||
/// model and in the Brain Parameters.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor that is expected by the model</param>
|
|||
/// <returns>If the Check failed, returns a string containing information about why the
|
|||
/// check failed. If the check passed, returns null.</returns>
|
|||
private string CheckVectorObsShape(Tensor tensor) |
|||
{ |
|||
var vecObsSizeBp = _brainParameters.vectorObservationSize; |
|||
var numStackedVector = _brainParameters.numStackedVectorObservations; |
|||
var totalVecObsSizeT = tensor.Shape[1]; |
|||
if (vecObsSizeBp * numStackedVector != totalVecObsSizeT) |
|||
{ |
|||
return string.Format( |
|||
"Vector Observation Size of the model does not match. " + |
|||
"Received {0} x {1} but was expecting {2}.", |
|||
vecObsSizeBp, numStackedVector, totalVecObsSizeT); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks that the shape of the Previous Vector Action input placeholder is the same in the
|
|||
/// model and in the Brain Parameters.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor that is expected by the model</param>
|
|||
/// <returns>If the Check failed, returns a string containing information about why the
|
|||
/// check failed. If the check passed, returns null.</returns>
|
|||
private string CheckPreviousActionShape(Tensor tensor) |
|||
{ |
|||
var numberActionsBp = _brainParameters.vectorActionSize.Length; |
|||
var numberActionsT = tensor.Shape[1]; |
|||
if (numberActionsBp != numberActionsT) |
|||
{ |
|||
return string.Format( |
|||
"Previous Action Size of the model does not match. " + |
|||
"Received {0} but was expecting {1}.", |
|||
numberActionsBp, numberActionsT); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks that the shape of the visual observation input placeholder is the same in the
|
|||
/// model and in the Brain Parameters.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor that is expected by the model</param>
|
|||
/// <param name="visObsIndex"> The index of the visual observation.</param>
|
|||
/// <returns>If the Check failed, returns a string containing information about why the
|
|||
/// check failed. If the check passed, returns null.</returns>
|
|||
private string CheckVisualObsShape(Tensor tensor, int visObsIndex) |
|||
{ |
|||
var resolutionBp = _brainParameters.cameraResolutions[visObsIndex]; |
|||
var widthBp = resolutionBp.width; |
|||
var heightBp = resolutionBp.height; |
|||
var pixelBp = resolutionBp.blackAndWhite ? 1 : 3; |
|||
var widthT = tensor.Shape[1]; |
|||
var heightT = tensor.Shape[2]; |
|||
var pixelT = tensor.Shape[3]; |
|||
if ((widthBp != widthT) || (heightBp != heightT) || (pixelBp != pixelT)) |
|||
{ |
|||
return string.Format( |
|||
"The visual Observation {0} of the model does not match. " + |
|||
"Received Tensor of shape [?x{1}x{2}x{3}] but was expecting [?x{4}x{5}x{6}].", |
|||
visObsIndex, widthBp, heightBp, pixelBp, widthT, heightT, pixelT); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates failed checks that correspond to output shapes incompatibilities between
|
|||
/// the model and the BrainParameters.
|
|||
/// </summary>
|
|||
/// <param name="isContinuous"> Whether the model is expecting continuous or
|
|||
/// discrete control.</param>
|
|||
/// <param name="modelActionSize"> The size of the action output that is expected
|
|||
/// by the model.</param>
|
|||
/// <returns>A IEnumerable of string corresponding to the incompatible shapes between
|
|||
/// model and BrainParameters.</returns>
|
|||
private void CheckOutputTensorShape(ModelActionType isContinuous, int modelActionSize) |
|||
{ |
|||
if (isContinuous == ModelActionType.Unknown) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"Cannot infer type of Control from the provided model."); |
|||
return; |
|||
} |
|||
if (isContinuous == ModelActionType.Continuous && |
|||
_brainParameters.vectorActionSpaceType != SpaceType.continuous) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"Model has been trained using Continuous Control but the Brain Parameters " + |
|||
"suggest Discrete Control."); |
|||
return; |
|||
} |
|||
if (isContinuous == ModelActionType.Discrete && |
|||
_brainParameters.vectorActionSpaceType != SpaceType.discrete) |
|||
{ |
|||
_failedModelChecks.Add( |
|||
"Model has been trained using Discrete Control but the Brain Parameters " + |
|||
"suggest Continuous Control."); |
|||
return; |
|||
} |
|||
var tensorTester = new Dictionary<string, Func<Tensor, int, string>>(); |
|||
if (_brainParameters.vectorActionSpaceType == SpaceType.continuous) |
|||
{ |
|||
tensorTester[TensorNames.ActionOutput] = CheckContinuousActionOutputShape; |
|||
} |
|||
else |
|||
{ |
|||
tensorTester[TensorNames.ActionOutput] = CheckDiscreteActionOutputShape; |
|||
} |
|||
// If the model expects an output but it is not in this list
|
|||
foreach (var tensor in GetOutputTensors()) |
|||
{ |
|||
if (tensorTester.ContainsKey(tensor.Name)) |
|||
{ |
|||
var tester = tensorTester[tensor.Name]; |
|||
var error = tester.Invoke(tensor, modelActionSize); |
|||
if (error != null) |
|||
{ |
|||
_failedModelChecks.Add(error); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks that the shape of the discrete action output is the same in the
|
|||
/// model and in the Brain Parameters.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor that is expected by the model</param>
|
|||
/// <param name="modelActionSize"> The size of the action output that is expected
|
|||
/// by the model.</param>
|
|||
/// <returns>If the Check failed, returns a string containing information about why the
|
|||
/// check failed. If the check passed, returns null.</returns>
|
|||
private string CheckDiscreteActionOutputShape(Tensor tensor, int modelActionSize) |
|||
{ |
|||
var bpActionSize = _brainParameters.vectorActionSize.Sum(); |
|||
if (modelActionSize != bpActionSize) |
|||
{ |
|||
return string.Format( |
|||
"Action Size of the model does not match. " + |
|||
"The BrainParameters expect {0} but the model contains {1}.", |
|||
bpActionSize, modelActionSize); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks that the shape of the continuous action output is the same in the
|
|||
/// model and in the Brain Parameters.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor that is expected by the model</param>
|
|||
/// <param name="modelActionSize"> The size of the action output that is expected
|
|||
/// by the model.</param>
|
|||
/// <returns>If the Check failed, returns a string containing information about why the
|
|||
/// check failed. If the check passed, returns null.</returns>
|
|||
private string CheckContinuousActionOutputShape(Tensor tensor, int modelActionSize) |
|||
{ |
|||
var bpActionSize = _brainParameters.vectorActionSize[0]; |
|||
if (modelActionSize != bpActionSize) |
|||
{ |
|||
return string.Format( |
|||
"Action Size of the model does not match. " + |
|||
"The BrainParameters expect {0} but the model contains {1}.", |
|||
bpActionSize, modelActionSize); |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 259e3a0e37204794a885219327bd4c02 |
|||
timeCreated: 1539197357 |
|
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using System.Collections.Generic; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
using System; |
|||
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// Mapping between the output Tensor names and the method that will use the
|
|||
/// output tensors and the Agents present in the batch to update their action, memories and
|
|||
/// value estimates.
|
|||
/// A TensorApplier implements a Dictionary of strings (node names) to an Action.
|
|||
/// This action takes as input the Tensor and the Dictionary of Agent to AgentInfo for
|
|||
/// the current batch.
|
|||
/// </summary>
|
|||
public class TensorApplier |
|||
{ |
|||
/// <summary>
|
|||
/// A tensor Applier's Execute method takes a Tensor and a Dictionary of Agent to AgentInfo.
|
|||
/// Uses the data contained inside the Tensor to modify the state of the Agent. The Tensors
|
|||
/// are assumed to have the batch size on the first dimension and the agents to be ordered
|
|||
/// the same way in the dictionary and in the Tensor.
|
|||
/// </summary>
|
|||
public interface Applier |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the values in the Tensor to the Agents present in the agentInfos
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The Tensor containing the data to be applied to the Agents</param>
|
|||
/// <param name="agentInfo"> Dictionary of Agents to AgentInfo that will reveive
|
|||
/// the values of the Tensor.</param>
|
|||
void Apply(Tensor tensor, Dictionary<Agent, AgentInfo> agentInfo); |
|||
} |
|||
|
|||
Dictionary<string, Applier> _dict = new Dictionary<string, Applier>(); |
|||
|
|||
/// <summary>
|
|||
/// Returns a new TensorAppliers object.
|
|||
/// </summary>
|
|||
/// <param name="bp"> The BrainParameters used to determine what Appliers will be
|
|||
/// used</param>
|
|||
/// <param name="seed"> The seed the Appliers will be initialized with.</param>
|
|||
public TensorApplier(BrainParameters bp, int seed) |
|||
{ |
|||
_dict[TensorNames.ValueEstimateOutput] = new ValueEstimateApplier(); |
|||
if (bp.vectorActionSpaceType == SpaceType.continuous) |
|||
{ |
|||
_dict[TensorNames.ActionOutput] = new ContinuousActionOutputApplier(); |
|||
} |
|||
else |
|||
{ |
|||
_dict[TensorNames.ActionOutput] = new DiscreteActionOutputApplier( |
|||
bp.vectorActionSize, seed); |
|||
} |
|||
_dict[TensorNames.RecurrentOutput] = new MemoryOutputApplier(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Updates the state of the agents based on the data present in the tensor.
|
|||
/// </summary>
|
|||
/// <param name="tensors"> Enumerable of tensors containing the data.</param>
|
|||
/// <param name="agentInfos"> Dictionary of Agent to AgentInfo that contains the
|
|||
/// Agents that will be updated using the tensor's data</param>
|
|||
/// <exception cref="UnityAgentsException"> One of the tensor does not have an
|
|||
/// associated applier.</exception>
|
|||
public void ApplyTensors( |
|||
IEnumerable<Tensor> tensors, Dictionary<Agent, AgentInfo> agentInfos) |
|||
{ |
|||
foreach (var tensor in tensors) |
|||
{ |
|||
if (!_dict.ContainsKey(tensor.Name)) |
|||
{ |
|||
throw new UnityAgentsException( |
|||
"Unknow tensor expected as output : "+tensor.Name); |
|||
} |
|||
_dict[tensor.Name].Apply(tensor, agentInfos); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d1bef4f4ae72645108f16614355473e8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using UnityEngine.MachineLearning.InferenceEngine; |
|||
using System.Collections.Generic; |
|||
using UnityEngine.MachineLearning.InferenceEngine.Util; |
|||
using System.Linq; |
|||
using System; |
|||
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// Mapping between Tensor names and generators.
|
|||
/// A TensorGenerator implements a Dictionary of strings (node names) to an Action.
|
|||
/// The Action take as argument the tensor, the current batch size and a Dictionary of
|
|||
/// Agent to AgentInfo corresponding to the current batch.
|
|||
/// Each Generator reshapes and fills the data of the tensor based of the data of the batch.
|
|||
/// When the Tensor is an Input to the model, the shape of the Tensor will be modified
|
|||
/// depending on the current batch size and the data of the Tensor will be filled using the
|
|||
/// Dictionary of Agent to AgentInfo.
|
|||
/// When the Tensor is an Output of the model, only the shape of the Tensor will be modified
|
|||
/// using the current batch size. The data will be prefilled with zeros.
|
|||
/// </summary>
|
|||
public class TensorGenerator |
|||
{ |
|||
public interface Generator |
|||
{ |
|||
/// <summary>
|
|||
/// Modifies the data inside a Tensor according to the information contained in the
|
|||
/// AgentInfos contained in the current batch.
|
|||
/// </summary>
|
|||
/// <param name="tensor"> The tensor the data and shape will be modified</param>
|
|||
/// <param name="batchSize"> The number of agents present in the current batch</param>
|
|||
/// <param name="agentInfo"> Dictionary of Agent to AgentInfo containing the
|
|||
/// information that will be used to populate the tensor's data</param>
|
|||
void Generate(Tensor tensor, int batchSize, Dictionary<Agent, AgentInfo> agentInfo); |
|||
} |
|||
|
|||
Dictionary<string, Generator> _dict = new Dictionary<string, Generator>(); |
|||
|
|||
/// <summary>
|
|||
/// Returns a new TensorGenerators object.
|
|||
/// </summary>
|
|||
/// <param name="bp"> The BrainParameters used to determine what Generators will be
|
|||
/// used</param>
|
|||
/// <param name="seed"> The seed the Generators will be initialized with.</param>
|
|||
public TensorGenerator(BrainParameters bp, int seed) |
|||
{ |
|||
// Generator for Inputs
|
|||
_dict[TensorNames.BatchSizePlaceholder] = new BatchSizeGenerator(); |
|||
_dict[TensorNames.SequenceLengthPlaceholder] = new SequenceLengthGenerator(); |
|||
_dict[TensorNames.VectorObservationPlacholder] = new VectorObservationGenerator(); |
|||
_dict[TensorNames.RecurrentInPlaceholder] = new RecurrentInputGenerator(); |
|||
_dict[TensorNames.PreviousActionPlaceholder] = new PreviousActionInputGenerator(); |
|||
_dict[TensorNames.ActionMaskPlaceholder] = new ActionMaskInputGenerator(); |
|||
_dict[TensorNames.RandomNormalEpsilonPlaceholder] = new RandomNormalInputGenerator(seed); |
|||
if (bp.cameraResolutions != null) |
|||
{ |
|||
for (var visIndex = 0; |
|||
visIndex < bp.cameraResolutions.Length; |
|||
visIndex++) |
|||
{ |
|||
var index = visIndex; |
|||
var bw = bp.cameraResolutions[visIndex].blackAndWhite; |
|||
_dict[TensorNames.VisualObservationPlaceholderPrefix + visIndex] = new |
|||
VisualObservationInputGenerator(index, bw); |
|||
} |
|||
} |
|||
|
|||
// Generators for Outputs
|
|||
_dict[TensorNames.ActionOutput] = new BiDimensionalOutputGenerator(); |
|||
_dict[TensorNames.RecurrentOutput] = new BiDimensionalOutputGenerator(); |
|||
_dict[TensorNames.ValueEstimateOutput] = new BiDimensionalOutputGenerator(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Populates the data of the tensor inputs given the data contained in the current batch
|
|||
/// of agents.
|
|||
/// </summary>
|
|||
/// <param name="tensors"> Enumerable of tensors that will be modified.</param>
|
|||
/// <param name="currentBatchSize"> The number of agents present in the current batch
|
|||
/// </param>
|
|||
/// <param name="agentInfos"> Dictionary of Agent to AgentInfo that contains the
|
|||
/// data that will be used to modify the tensors</param>
|
|||
/// <exception cref="UnityAgentsException"> One of the tensor does not have an
|
|||
/// associated generator.</exception>
|
|||
public void GenerateTensors(IEnumerable<Tensor> tensors, |
|||
int currentBatchSize, |
|||
Dictionary<Agent, AgentInfo> agentInfos) |
|||
{ |
|||
foreach (var tensor in tensors) |
|||
{ |
|||
if (!_dict.ContainsKey(tensor.Name)) |
|||
{ |
|||
throw new UnityAgentsException( |
|||
"Unknow tensor expected as input : " + tensor.Name); |
|||
} |
|||
_dict[tensor.Name].Generate(tensor, currentBatchSize, agentInfos); |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6a24e86bc77c4a5088a5fd04d6d30e81 |
|||
timeCreated: 1537484304 |
|
|||
namespace MLAgents.InferenceBrain |
|||
{ |
|||
/// <summary>
|
|||
/// Contains the names of the input and output Tensor for the Inference Brain.
|
|||
/// </summary>
|
|||
public static class TensorNames |
|||
{ |
|||
public const string BatchSizePlaceholder = "batch_size"; |
|||
public const string SequenceLengthPlaceholder = "sequence_length"; |
|||
public const string VectorObservationPlacholder = "vector_observation"; |
|||
public const string RecurrentInPlaceholder = "recurrent_in"; |
|||
public const string VisualObservationPlaceholderPrefix = "visual_observation_"; |
|||
public const string PreviousActionPlaceholder = "prev_action"; |
|||
public const string ActionMaskPlaceholder = "action_masks"; |
|||
public const string RandomNormalEpsilonPlaceholder = "epsilon"; |
|||
|
|||
public const string ValueEstimateOutput = "value_estimate"; |
|||
public const string RecurrentOutput = "recurrent_out"; |
|||
public const string MemorySize = "memory_size"; |
|||
public const string VersionNumber = "version_number"; |
|||
public const string IsContinuousControl = "is_continuous_control"; |
|||
public const string ActionOutputShape = "action_output_shape"; |
|||
public const string ActionOutput = "action"; |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: b28a46ea97c2445794d29d5a8a718a4a |
|||
timeCreated: 1538158527 |
撰写
预览
正在加载...
取消
保存
Reference in new issue