155 строки
6.2 KiB
C#
155 строки
6.2 KiB
C#
// Copyright (c) Microsoft Corporation.
|
||
// Licensed under the MIT license.
|
||
|
||
using MathNet.Numerics.LinearAlgebra;
|
||
using HEWrapper;
|
||
using System.Linq;
|
||
|
||
namespace NeuralNetworks
|
||
{
|
||
public class LLPoolLayer : BaseLayer
|
||
{
|
||
ConvolutionEngine convolutionEngine = new ConvolutionEngine();
|
||
double[] _Weights = null;
|
||
public double[] Weights { get { return _Weights; } set { _Weights = value; layerPrepared = false; } }
|
||
double[] _Bias = null;
|
||
public double[] Bias { get { return _Bias; } set { _Bias = value; layerPrepared = false; } }
|
||
public int[] InputShape { get { return convolutionEngine.InputShape; } set { convolutionEngine.InputShape = value; layerPrepared = false; } }
|
||
public int[] KernelShape { get { return convolutionEngine.KernelShape; } set { convolutionEngine.KernelShape = value; layerPrepared = false; } }
|
||
public double WeightsScale { get; set; } = 1.0;
|
||
|
||
public override double GetOutputScale()
|
||
{
|
||
return ((Weights == null) ? Offsets.Length : WeightsScale) * Source.GetOutputScale();
|
||
}
|
||
public int[] Stride { get { return convolutionEngine.Stride; } set { convolutionEngine.Stride = value; layerPrepared = false; } }
|
||
public bool[] Padding { get { return convolutionEngine.Padding; } set { convolutionEngine.Padding = value; layerPrepared = false; } }
|
||
public int[] Upperpadding { get { return convolutionEngine.Upperpadding; } set { convolutionEngine.Upperpadding = value; layerPrepared = false; } }
|
||
public int[] Lowerpadding { get { return convolutionEngine.Lowerpadding; } set { convolutionEngine.Lowerpadding = value; layerPrepared = false; } }
|
||
public int[] MapCount { get { return convolutionEngine.MapCount; } set { convolutionEngine.MapCount = value; layerPrepared = false; } }
|
||
private int kernelSize = -1; // the value -1 is used such that it will throw an exception if it was not computed
|
||
|
||
IVector[] weightWindows = null;
|
||
IVector[] biasVectors = null;
|
||
int[][] Offsets { get { return convolutionEngine.Offsets; } }
|
||
int[][] Corners { get { return convolutionEngine.Corners; } }
|
||
|
||
public Vector<double> HotIndices { get; set; } = null;
|
||
|
||
public override void Dispose()
|
||
{
|
||
if (weightWindows != null)
|
||
foreach (var w in weightWindows)
|
||
if (w != null) w.Dispose();
|
||
if (biasVectors != null)
|
||
foreach (var b in biasVectors)
|
||
if (b != null) b.Dispose();
|
||
weightWindows = null;
|
||
biasVectors = null;
|
||
|
||
|
||
}
|
||
public override void Prepare()
|
||
{
|
||
if (!layerPrepared)
|
||
{
|
||
convolutionEngine.Prepare();
|
||
kernelSize = KernelShape.Aggregate(1, (acc, val) => acc * val);
|
||
if (Bias == null) kernelSize++;
|
||
if (Weights == null) return;
|
||
PrepareWeightsWindows();
|
||
double BiasScale = GetOutputScale();
|
||
int maps = (MapCount == null) ? 1 : MapCount.Aggregate(1, (acc, val) => acc * val);
|
||
if (HotIndices == null)
|
||
{
|
||
HotIndices = Vector<double>.Build.Dense(Corners.Length) + 1;
|
||
}
|
||
if (Bias != null)
|
||
{
|
||
biasVectors = new IVector[maps];
|
||
ParallelProcessInEnv(maps, (env, taskIndex, mapIndex) =>
|
||
{
|
||
biasVectors[mapIndex] = Factory.GetPlainVector(HotIndices * Bias[mapIndex], EVectorFormat.dense, Source.GetOutputScale() * WeightsScale);
|
||
});
|
||
}
|
||
else
|
||
{
|
||
biasVectors = new IVector[maps];
|
||
ParallelProcessInEnv(maps, (env, taskIndex, mapIndex) =>
|
||
{
|
||
biasVectors[mapIndex] = Factory.GetPlainVector(HotIndices * Weights[(mapIndex + 1) * kernelSize - 1], EVectorFormat.dense, Source.GetOutputScale() * WeightsScale);
|
||
});
|
||
|
||
}
|
||
|
||
layerPrepared = true;
|
||
}
|
||
}
|
||
|
||
|
||
double ElementAt(double[] w, int[] Corner, int[] offset, int[] shape, int bias = 0)
|
||
{
|
||
var l = convolutionEngine.Location(Corner, offset, shape, bias);
|
||
if (l < 0) return 0;
|
||
return w[l];
|
||
}
|
||
|
||
|
||
|
||
|
||
void PrepareWeightsWindows()
|
||
{
|
||
int maps = (MapCount == null) ? 1 : MapCount.Aggregate(1, (acc, val) => acc * val);
|
||
|
||
weightWindows = new IVector[maps];
|
||
for (int m = 0; m < maps; m++)
|
||
{
|
||
var w = Offsets.Select(offset => ElementAt(Weights, null, offset, KernelShape, m * kernelSize));
|
||
weightWindows[m] = Factory.GetPlainVector(Vector<double>.Build.DenseOfEnumerable(w), EVectorFormat.sparse, WeightsScale);
|
||
}
|
||
}
|
||
|
||
public override IMatrix Apply(IMatrix m)
|
||
{
|
||
if (Weights == null) // pool without convolve
|
||
{
|
||
IVector vec = null;
|
||
ProcessInEnv(env =>
|
||
{
|
||
|
||
|
||
vec = Enumerable.Range(0, (int)m.ColumnCount).Select(i => m.GetColumn(i)).Aggregate((v1, v2) => v1.Add(v2, env));
|
||
vec.RegisterScale(vec.Scale * m.ColumnCount);
|
||
});
|
||
return Factory.GetMatrix(new IVector[] { vec }, EMatrixFormat.ColumnMajor, CopyVectors: false);
|
||
}
|
||
else
|
||
{
|
||
int maps = biasVectors.Length;
|
||
IVector[] res = new IVector[maps];
|
||
ParallelProcessInEnv(maps, (env, task, k) =>
|
||
{
|
||
using (var mul = m.Mul(weightWindows[k], env))
|
||
res[k] = mul.Add(biasVectors[k], env);
|
||
});
|
||
return Factory.GetMatrix(res, EMatrixFormat.ColumnMajor, CopyVectors: false);
|
||
}
|
||
}
|
||
|
||
|
||
public override int OutputDimension()
|
||
{
|
||
if (!layerPrepared) Prepare();
|
||
int count = Corners.Length;
|
||
if (Weights == null)
|
||
{
|
||
return count;
|
||
}
|
||
|
||
int maps = (MapCount == null) ? 1 : MapCount.Aggregate(1, (acc, val) => acc * val);
|
||
return count * maps;
|
||
|
||
}
|
||
}
|
||
}
|