public void ComputeGraduateInThread(object input) { ThreadInputDatum inputDatum = (ThreadInputDatum) input; int lastLayerId = this.LayerCount - 1; foreach (double[] t in inputDatum.trainDatum) { // no need to visit in random order because all rows processed before any updates ('batch') Array.Copy(t, inputDatum.xValues, this.Sizes[0]); // get the inputs // get the target values Array.Copy(t, this.Sizes[0], inputDatum.tValues, 0, this.Sizes[lastLayerId]); // copy xValues in, compute outputs using curr weights (and store outputs internally) double[] yValues = this.ComputeOutputs(inputDatum.xValues, inputDatum.field); double[][] gradTerms = this.CalculateGradTerms(inputDatum.field, inputDatum.tValues); for (int layer = lastLayerId; layer > 0; layer--) { // add input to h-o component to make h-o weight gradients, and accumulate for (int j = 0; j < this.Sizes[layer]; ++j) { double grad = gradTerms[layer][j]; inputDatum.allGradsAcc[layer].Biases[j] += grad; for (int i = 0; i < this.Sizes[layer - 1]; ++i) { grad = gradTerms[layer][j] * inputDatum.field[layer - 1][i]; inputDatum.allGradsAcc[layer].Weights[j][i] += grad; } } } for (int j = 0; j < this.Sizes[lastLayerId]; ++j) { double err = Math.Pow(yValues[j] - inputDatum.tValues[j], 2); inputDatum.sumSquaredErrors[0] += err * inputDatum.delim1; inputDatum.sumSquaredErrors[1] += err * inputDatum.delim2; } } }
/** * Подсчитываем градиент в несколько потоков */ protected double[] ComputeGraduateMultiThread(double[][] trainData, WeightComposite[] allGradsAcc) { TaskFactory taskFactory = new TaskFactory(); Task[] tasks = new Task[this.threadCount]; ThreadInputDatum[] threadInputData = new ThreadInputDatum[this.threadCount]; for (int i = 0; i < this.threadCount; i++) { threadInputData[i].field = new double[this.LayerCount][]; threadInputData[i].allGradsAcc = new WeightComposite[this.LayerCount]; threadInputData[i].xValues = new double[this.Sizes[0]]; // inputs threadInputData[i].tValues = new double[this.Sizes[this.LayerCount - 1]]; // targets threadInputData[i].delim1 = 1.0 / trainData.Length; threadInputData[i].delim2 = 1.0 / trainData.Length / this.Sizes[this.LayerCount - 1]; threadInputData[i].sumSquaredErrors = new double[] {0, 0}; for (int j = 0; j < this.LayerCount; j++) { threadInputData[i].field[j] = new double[this.Sizes[j]]; if (j <= 0) continue; threadInputData[i].allGradsAcc[j].Biases = new double[this.Sizes[j]]; threadInputData[i].allGradsAcc[j].Weights = MakeMatrix(this.Sizes[j], this.Sizes[j - 1], 0.0); } } List<double[]> innerTrainData = new List<double[]>(trainData); List<double[]> innerTrainDataChunk = new List<double[]>(); int chunk_size = (int) (innerTrainData.Count * 0.8 / this.threadCount); while (innerTrainData.Count > 0) { int currentThread = -1; for (int i = 0; i < this.threadCount; i++) { // ReSharper disable once InvertIf if ((tasks[i] == null) || tasks[i].IsCompleted) { currentThread = i; break; } } if (currentThread == -1) { Thread.Sleep(20); continue; } innerTrainDataChunk.Clear(); while ((innerTrainDataChunk.Count < chunk_size) && (innerTrainData.Count > 0)) { innerTrainDataChunk.Add(innerTrainData[0]); innerTrainData.RemoveAt(0); } threadInputData[currentThread].trainDatum = innerTrainDataChunk.ToArray(); for (int layer = 1; layer < this.LayerCount; layer++) { // zero-out values from prev iteration ZeroOut(threadInputData[currentThread].allGradsAcc[layer].Weights); ZeroOut(threadInputData[currentThread].allGradsAcc[layer].Biases); } tasks[currentThread] = taskFactory.StartNew(this.ComputeGraduateInThread, threadInputData[currentThread]); } for (int i = 0; i < this.threadCount; i++) { if (tasks[i] != null) { tasks[i].Wait(); } } // Всё в allGradsAcc for (int i = 0; i < this.threadCount; i++) { for (int layer = 1; layer < this.LayerCount; layer++) { for (int size = 0; size < this.Sizes[layer]; size++) { allGradsAcc[layer].Biases[size] += threadInputData[i].allGradsAcc[layer].Biases[size]; for (int prev_size = 0; prev_size < this.Sizes[layer - 1]; prev_size++) { allGradsAcc[layer].Weights[size][prev_size] += threadInputData[i].allGradsAcc[layer].Weights[size][prev_size]; } } } } double sumSquaredErrorItem = 0; double sumSquaredError = 0; for (int i = 0; i < this.threadCount; i++) { sumSquaredError += threadInputData[i].sumSquaredErrors[0]; sumSquaredErrorItem += threadInputData[i].sumSquaredErrors[1]; } double[] d = {Math.Sqrt(sumSquaredErrorItem), Math.Sqrt(sumSquaredError)}; return d; }