我正在研究.Net开发的遗传机器学习项目(而不是Matlab - My Norm) . 我不是专业.net编码器,所以借口任何noobish实现 .
该项目本身是巨大的所以我不会厌倦你的全部细节,但基本上人口神经网络(如决策树)的每一个都在一个问题域进行评估,在这种情况下使用感知输入流 . 允许人口中表现最佳的人群繁殖和 生产环境 后代(继承父母双方的倾向),表现不佳的人群被杀死或繁殖出来 . 继续进化直到找到可接受的解决方案 . 一旦找到,最终演变的“网络”将从实验室中提取出来并置于轻量级的实际应用程序中 . 该技术可用于开发非常复杂的控制解决方案,这对于正常编程几乎不可能或太耗时,如自动驾驶汽车,机械稳定性控制,数据中心负载 balancer 等 .
无论如何,该项目到目前为止取得了巨大的成功,并且产生了惊人的结果,但唯一的问题是,一旦我转向更大的数据集,性能就会非常缓慢 . 我希望只是我的代码,所以非常感谢一些专家的帮助 .
在这个项目中,收敛到接近理想的解决方案通常需要大约7天的处理时间!只需对参数进行一些调整并等待结果就太痛苦了 .
基本上,多个并行线程需要读取非常大的数据集的连续部分(加载后数据不会更改) . 该数据集包含连续约300至1000个双打和超过500k行的任何内容 . 由于数据集可能超过.Net对象限制2GB,因此无法存储在普通的2d数组中 - 最简单的方法是使用单个数组的通用列表 .
并行可扩展性似乎是一个很大的限制因素,因为在具有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; }
}
}
}
}
1 回答
首先,回答任何性能问题的唯一方法是分析应用程序 . 我正在使用VS 2012内置的分析器 - 还有其他的https://stackoverflow.com/a/100490/19624
从最初的代码读取,即静态分析,我跳出来的唯一一件事就是在循环中不断重新分配Temp;这不是有效的,如果可能的话,需要移出循环 .
使用分析器,您可以看到发生了什么:
我首先使用您发布的代码进行了描述(对于您发布问题的完整可编辑示例,如果您还没有,我现在不会回答这个问题) .
这告诉我批量是在循环的内部,我将分配移动到Parallel.ForEach循环 .
所以我从上面可以看出,重新分配有4.4%的浪费;但可能不足为奇的是,内循环占87.6% .
这带我进入我的第一个优化规则,即首先检查算法而不是优化代码 . 良好算法的不良实现通常比高度优化的差算法快 .
删除Temp的重复分配会略微改变图片;
通过指定并行性也值得调整一下;我发现Parallel.ForEach足以满足我的需求,但是再次通过手动将工作分区为队列可以获得更好的结果 .
虽然在运行中我得到了我在CPU使用方面的预期:虽然我的机器也在运行另一个漫长的过程基本级别(下图中的峰值是分析此程序时) .
总结一下
查看最常执行的部分,并在可能的情况下提出新算法 .
目标计算机上的配置文件
只有当您确定上述(1)时才值得考虑优化算法;考虑以下因素a)代码优化b)内存调整/分区数据以保持缓存c)线程使用的改进