自我训练算法

我想针对特定问题开发自我训练算法。 为了简单起见,我将把它归结为简单的例子。

更新:我已经添加了一个工作解决方案作为下面这个问题的答案。

假设我有一个来自数据库的大量实体列表。 每个实体属于同一类型,并具有4个byte类型的属性。

public class Entity { public byte Prop1 { get; set; } public byte Prop2 { get; set; } public byte Prop3 { get; set; } public byte Prop4 { get; set; } } 

现在,我想根据一个简单的条件动态测试每个实体的一个或多个属性。 这基本上意味着我想针对这种情况测试所有属性的所有可能组合。

为了完成这项工作,我为属性创建了一个位掩码。

 [Flags] public enum EEntityValues { Undefined = 0, Prop1 = 1, Prop2 = 2, Prop3 = 4, Prop4 = 8, } 

并添加了一种获取位掩码最大值的方法。 对于此示例,返回15(1 + 2 + 4 + 8)。

 public static int GetMaxValue() where T : struct { return Enum.GetValues( typeof(T) ).Cast().Sum(); } 

在这个阶段,我能够通过简单的循环遍历所有属性组合。 在第一次迭代中的示例中,测试属性Prop1,在第二次迭代上测试Prop2,在第三次迭代上测试Prop1和Prop2,依此类推。

 for(int i = 1; i <= GetMaxValue(); i++) { EEntityValues flags = (EEntityValues)i; if(flags.HasSet(EEntityValues.Prop1)) { .... } } 

现在让我们让实体进入游戏。

 List entities = GetEntitiesFromDb(); for(int i = 1; i <= GetMaxValue(); i++) { EEntityValues flags = (EEntityValues)i; byte minProp1Value = 10, minProp2Value = 20, minProp3Value = 30, minProp4Value = 40; foreach(Entitiy entity in entities) { if(flags.HasSet(EEntityValues.Prop1) && entitiy.Prop1 >= minProp1Value) { .... } else { continue; } if(flags.HasSet(EEntityValues.Prop2) && entitiy.Prop2 >= minProp2Value) { .... } else { continue; } } } 

好吧,如果我的最小值是静态的,这很有效。

现在让我们变得更复杂。 我们记得,在第一次迭代中,我们仅测试属性Prop1,因为位掩码是1.Prot1的值范围是0..255。 我还为此属性定义了一个最小值,其有效范围为1..255。 此最小值只是跳过foreach循环中实体的filter。

现在我想测试属性Prop1,而我正在提升最低水平。 这些测试不是问题的一部分,因此我不将它们包含在我的样本中。

  byte minProp1Value = 1; while(minProp1Value = minProp1Value) { .... // Testing the matching entity and storing the result } else { continue; } } minProp1Value++; } 

这对于单个属性来说很简单。 在第三次迭代中,我必须处理2个属性Prop1和Prop2,因为位掩码是3。

  byte minProp1Value = 1, minProp2Value = 1; while(minProp1Value <= 255) { while(minProp2Value <= 255) { foreach(Entitiy entity in entities) { .... } minProp2Value++; } minProp1Value++; minProp2Value = 1; } 

正如您所看到的,在这个阶段,我正在测试每个实体的Prop1和Prop2,以达到不断上升的最低水平。

由于我正在处理动态生成的多个属性集,我无法将while循环硬编码到我的代码中。 这就是为什么我正在寻找一种更智能的解决方案来测试给定属性集(位掩码)的所有可能的最小值组合。

rest后,我想出了一个似乎符合我要求的解决方案。 限制是所有测试的属性应该是具有相同值范围的相同类型,在我的情况下这对我来说很好,因为所有属性都是抽象百分比值。

顺便说一句,我不确定“自我训练算法”这个主题在这里是否有点误导。 有几种方法可以实现这样的解决方案,但如果您不知道数据的行为方式以及值的影响,最简单的解决方案是强制所有可能的组合以确定最佳拟合结果。 这就是我在这里展示的内容。

无论如何,为了测试目的,我在我的实体类中添加了一个随机数生成器。

 public class Entity { public byte Prop1 { get; set; } public byte Prop2 { get; set; } public byte Prop3 { get; set; } public byte Prop4 { get; set; } public Entity() { Random random = new Random( Guid.NewGuid().GetHashCode() ); byte[] bytes = new byte[ 4 ]; random.NextBytes( bytes ); this.Prop1 = bytes[0]; this.Prop2 = bytes[1]; this.Prop3 = bytes[2]; this.Prop4 = bytes[3]; } } 

我的位掩码保持不变。

 [Flags] public enum EProperty { Undefined = 0, Prop1 = 1, Prop2 = 1 << 1, Prop3 = 1 << 2, Prop4 = 1 << 3 } 

比我添加了一些新的扩展方法来处理我的位掩码。

 public static class BitMask { public static int GetMaxValue() where T : struct { return Enum.GetValues(typeof (T)).Cast().Sum(); } public static int GetTotalCount() where T : struct { return Enum.GetValues(typeof (T)).Cast().Count(e => e > 0); } public static int GetFlagCount(this T mask) where T : struct { int result = 0, value = (int) (object) mask; while (value != 0) { value = value & (value - 1); result++; } return result; } public static IEnumerable Split(this T mask) { int maskValue = (int) (object) mask; foreach (T flag in Enum.GetValues(typeof (T))) { int flagValue = (int) (object) flag; if (0 != (flagValue & maskValue)) { yield return flag; } } } } 

比我写了一个查询生成器

 public static class QueryBuilder { public static IEnumerable Where(this IEnumerable entities, EProperty[] properties, int[] values) { IEnumerable result = entities.Select(e => e); for (int index = 0; index <= properties.Length - 1; index++) { EProperty property = properties[index]; int value = values[index]; switch (property) { case EProperty.Prop1: result = result.Where(e => Math.Abs(e.Prop1) >= value); break; case EProperty.Prop2: result = result.Where(e => Math.Abs(e.Prop2) >= value); break; case EProperty.Prop3: result = result.Where(e => Math.Abs(e.Prop3) >= value); break; case EProperty.Prop4: result = result.Where(e => Math.Abs(e.Prop4) >= value); break; } } return result; } } 

最后我准备开始训练了。

  private const int maxThreads = 10; private const int minValue = 0; private const int maxValue = 100; private static IEnumerable entities; public static void Main(string[] args) { Console.WriteLine(DateTime.Now.ToLongTimeString()); entities = Enumerable.Repeat(new Entity(), 10).ToList(); Action testCase = RunTestCase; RunSelfTraining( testCase ); Console.WriteLine(DateTime.Now.ToLongTimeString()); Console.WriteLine("Done."); Console.Read(); } private static void RunTestCase( EProperty[] properties, int[] values ) { foreach( Entity entity in entities.Where( properties, values ) ) { } } private static void RunSelfTraining( Action testCase ) where T : struct { ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = maxThreads }; for (int maskValue = 1; maskValue <= BitMask.GetMaxValue(); maskValue++) { T mask = ( T ) (object)maskValue; T[] properties = mask.Split().ToArray(); int variations = (int) Math.Pow(maxValue - minValue + 1, properties.Length); Parallel.For(1, variations, parallelOptions, variation => { int[] values = GetVariation(variation, minValue, maxValue, properties.Length).ToArray(); testCase.Invoke(properties, values); } ); } } public static IEnumerable GetVariation( int index, int minValue, int maxValue, int count ) { index = index - 1; int range = maxValue - minValue + 1; for( int j = 0; j < count; j++ ) { yield return index % range + minValue; index = index / range; } } }