我正在开发一个用 .Net 开发的遗传机器学习项目(与 Matlab – My Norm 相对)。我不是专业的 .net 编码器,所以请原谅任何愚蠢的实现。
该项目本身是巨大的,所以我不会让你厌烦完整的细节,但基本上是一组人工神经网络(如决策树)都在一个问题域上进行评估,在这种情况下,该问题域使用感官输入流。种群中表现最好的人被允许繁殖和生产后代(继承父母双方的倾向),表现不佳的人被杀死或从种群中繁殖出来。进化继续进行,直到找到可接受的解决方案。一旦找到,最终进化的“网络”就会从实验室中提取出来,并放置在一个轻量级的现实世界应用程序中。该技术可用于开发非常复杂的控制解决方案,这些解决方案几乎不可能或太耗时而无法正常编程,例如自动驾驶汽车、机械稳定性控制、数据中心负载平衡等。
无论如何,到目前为止,该项目取得了巨大的成功,并产生了惊人的结果,但唯一的问题是,一旦我转移到更大的数据集,性能就会非常缓慢。我希望这只是我的代码,所以非常感谢一些专家的帮助。
在这个项目中,收敛到接近理想的解决方案通常需要大约 7 天的处理时间!只是对参数进行一点调整并等待结果太痛苦了。
基本上,多个并行线程需要读取一个非常大的数据集的连续部分(数据一旦加载就不会改变)。该数据集由大约 300 到 1000 个连续的 Doubles 和超过 500k 行的任何内容组成。由于数据集可以超过 2GB 的 .Net 对象限制,它不能存储在普通的二维数组中——最简单的方法是使用单个数组的通用列表。
并行可扩展性似乎是一个很大的限制因素,因为在具有 32 个 Xeon 内核的服务器上运行代码,通常早餐吃大数据集并不会比 Corei3 桌面产生太多的性能提升!
随着内核数量的增加,性能提升会迅速减少。
通过分析代码(以我有限的知识),我得到的印象是从多个线程读取数据集存在大量争用。
我尝试过使用 Jagged 数组和各种并发集合来尝试不同的数据集实现,但无济于事。
我为基准测试编写了一段快速而肮脏的代码,它与原始代码的核心实现类似,但仍然表现出类似的读取性能问题和并行可伸缩性问题。
任何想法或建议将不胜感激或确认这是我将得到的最好的。
非常感谢
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
//Benchmark script to time how long it takes to read dataset per iteration
namespace Benchmark_Simple
{
class Program
{
public static TrainingDataSet _DataSet;
public static int Features = 100; //Real test will require 300+
public static int Rows = 200000; //Real test will require 500K+
public static int _PopulationSize = 500; //Real test will require 1000+
public static int _Iterations = 10;
public static List<NeuralNetwork> _NeuralNetworkPopulation = new List<NeuralNetwork>();
static void Main()
{
Stopwatch _Stopwatch = new Stopwatch();
//Create Dataset
Console.WriteLine("Creating Training DataSet");
_DataSet = new TrainingDataSet(Features, Rows);
Console.WriteLine("Finished Creating Training DataSet");
//Create Neural Network Population
for (int i = 0; i <= _PopulationSize - 1; i++)
{
_NeuralNetworkPopulation.Add(new NeuralNetwork());
}
//Main Loop
for (int i = 0; i <= _Iterations - 1; i++)
{
_Stopwatch.Restart();
Parallel.ForEach(_NeuralNetworkPopulation, _Network => { EvaluateNetwork(_Network); });
//######## Removed for simplicity ##########
//Run Evolutionary Genetic Algorithm on population - I.E. Breed the strong, kill of the weak
//##########################################
//Repeat until acceptable solution is found
Console.WriteLine("Iteration time: {0}", _Stopwatch.ElapsedMilliseconds / 1000);
_Stopwatch.Stop();
}
Console.ReadLine();
}
private static void EvaluateNetwork(NeuralNetwork Network)
{
//Evaluate network on 10% of the Training Data at a random starting point
double Score = 0;
Random Rand = new Random();
int Count = (Rows / 100) * 10;
int RandonStart = Rand.Next(0, Rows - Count);
//The data must be read sequentially
for (int i = RandonStart; i <= RandonStart + Count; i++)
{
double[] NetworkInputArray = _DataSet.GetDataRow(i);
//####### Dummy Evaluation - just give it somthing to do for the sake of it
double[] Temp = new double[NetworkInputArray.Length + 1];
for (int j = 0; j <= NetworkInputArray.Length - 1; j++)
{
Temp[j] = Math.Log(NetworkInputArray[j] * Rand.NextDouble());
}
Score += Rand.NextDouble();
//##################
}
Network.Score = Score;
}
public class TrainingDataSet
{
//Simple demo class of fake data for benchmarking
private List<double[]> DataList = new List<double[]>();
public TrainingDataSet(int Features, int Rows)
{
Random Rand = new Random();
for (int i = 1; i <= Rows; i++)
{
double[] NewRow = new double[Features];
for (int j = 0; j <= Features - 1; j++)
{
NewRow[j] = Rand.NextDouble();
}
DataList.Add(NewRow);
}
}
public double[] GetDataRow(int Index)
{
return DataList[Index];
}
}
public class NeuralNetwork
{
//Simple Class to represent a dummy Neural Network -
private double _Score;
public NeuralNetwork()
{
}
public double Score
{
get { return _Score; }
set { _Score = value; }
}
}
}
}