如何在CNTK(C#)中为循环神经网络创建批量输入数据?
问题解决:CNTK中构造RNN批量输入并正确执行Evaluate
问题说明
需要构造形状为(输入维度5, 序列长度50, 批量大小100)的RNN输入数据,让model.Evaluate一次处理100组序列输入。当前代码会输出50次网络结果,而非将单组数据作为完整序列处理,且不清楚如何用Value对象创建三维输入张量。
核心问题分析
- 输入变量未定义动态轴:原代码的
inputVariable仅指定静态维度(输入维度5),缺少CNTK识别序列和批量所需的序列轴和批量轴,导致模型将每一步数据当作独立输入处理。 - 数据构造格式错误:原代码仅构造单组序列的
NDArrayView数组,未将100组序列组织成批量格式。 - LSTM层依赖动态轴实现循环:LSTM的循环逻辑需要通过动态轴识别序列时间步,缺少动态轴会导致无法正确展开循环。
修正步骤
1. 定义动态轴
添加序列轴和批量轴,明确区分序列维度与批量维度:
var sequenceAxis = new Axis("SequenceAxis"); var batchAxis = new Axis("BatchAxis");
2. 修正输入变量定义
创建包含静态维度+动态轴的输入变量:
var inputVariable = Variable.InputVariable( new int[] { inputDim }, DataType.Float, new List<Axis> { sequenceAxis, batchAxis } );
3. 正确构造批量输入数据
将100组序列(每组50步×5维)转换为CNTK可识别的批量格式,用Value.CreateBatch生成批量Value对象:
float[] flatInputs = new float[inputSequences * sequenceLength * inputDim]; int idx = 0; for (int batch = 0; batch < inputSequences; batch++) { for (int step = 0; step < sequenceLength; step++) { Array.Copy(inputs[batch][step], 0, flatInputs, idx, inputDim); idx += inputDim; } } var inputValue = Value.CreateBatch( inputVariable.Shape, flatInputs, inputVariable.DynamicAxes, cpu_device ); inputValue = inputValue.Clone(gpu_device);
4. 执行Evaluate获取批量结果
使用修正后的输入数据执行Evaluate,输出将对应100组序列的最终结果。
完整修正代码
var cpu_device = DeviceDescriptor.CPUDevice; var gpu_device = DeviceDescriptor.GPUDevice(0); var model_device = gpu_device; int inputDim = 5; int cellDim = 5; int outputDim = 3; int sequenceLength = 50; int inputSequences = 100; // 定义动态轴 var sequenceAxis = new Axis("SequenceAxis"); var batchAxis = new Axis("BatchAxis"); NDShape inputShape = NDShape.CreateNDShape(new int[] { inputDim }); NDShape outputShape = NDShape.CreateNDShape(new int[] { outputDim }); // 带动态轴的输入变量 var inputVariable = Variable.InputVariable( inputShape, DataType.Float, new List<Axis> { sequenceAxis, batchAxis } ); var outputVariable = Variable.InputVariable(outputShape, DataType.Float); var lstmLayer = CntkWrapper.Layers.LSTM<float>(cellDim, inputVariable, model_device); var model = CntkWrapper.Layers.Dense<float>(outputDim, lstmLayer, CNTKLib.Sigmoid, model_device); Random random = new Random(); // 构造三维输入数据:[批量数, 序列长度, 输入维度] var inputs = new float[inputSequences][][]; for(int i = 0; i < inputs.Length; i++) { inputs[i] = new float[sequenceLength][]; for(int k = 0; k < inputs[i].Length; k++) { inputs[i][k] = new float[inputDim]; for(int p = 0; p < inputs[i][k].Length; p++) { inputs[i][k][p] = (float)random.NextDouble(); } } } // 展平数据用于批量创建Value float[] flatInputs = new float[inputSequences * sequenceLength * inputDim]; int idx = 0; for (int batch = 0; batch < inputSequences; batch++) { for (int step = 0; step < sequenceLength; step++) { Array.Copy(inputs[batch][step], 0, flatInputs, idx, inputDim); idx += inputDim; } } // 创建批量输入并转移到GPU var inputValue = Value.CreateBatch( inputVariable.Shape, flatInputs, inputVariable.DynamicAxes, cpu_device ); inputValue = inputValue.Clone(gpu_device); // 执行评估 var inputDataMap = new Dictionary<Variable, Value>() { { inputVariable, inputValue } }; var outputDataMap = new Dictionary<Variable, Value>() { { model.Output, null } }; model.Evaluate(inputDataMap, outputDataMap, gpu_device); var outputValue = outputDataMap[model.Output]; // 结果为100组,每组对应一个序列的最终输出 IList<IList<float>> actualLabelSoftMax = outputValue.GetDenseData<float>(model.Output);
Layers类无需修改
原Layers类中的LSTM实现已正确处理循环逻辑,只要输入变量包含正确动态轴即可正常工作。
验证结果
修正后,actualLabelSoftMax长度为100,每组是3维结果(匹配outputDim=3),实现了一次Evaluate处理100组RNN输入的需求。
内容的提问来源于stack exchange,提问作者Христиан Белов
相关产品推荐
相关产品推荐

