/// <summary> /// 创建一个二维的单位矩阵 /// </summary> /// <param name="width">矩阵的宽度和高度</param> /// <returns></returns> public static TensorOld I(int width) { var result = new TensorOld(width, width); for (int i = 0; i < width; i++) { result.SetValueFast(1, i, i); } return(result); }
private static bool RowEqual(TensorOld t1, TensorOld t2, int start, int len) { for (int i = 0; i < len; i++) { if (t1.GetRawValues()[start + i] != t2.GetRawValues()[start + i]) { return(false); } } return(true); }
/// <summary> /// 当前Tensor和t的点积,结果保存在当前Tensor,要求两个Tensor结构一致 /// </summary> /// <param name="t">乘上的Tensor</param> /// <returns>当前Tensor</returns> public TensorOld MultipleElementWise(TensorOld t) { if (t.ElementCount == 1) { return(Multiple(t.GetValue())); } CheckShape(shape, t.shape); MultipleElementWise(this, t, this); return(this); }
/// <summary> /// 把t加到当前Tensor,t和当前Tensor必须要有相同的结构 /// </summary> /// <param name="t">要加的Tensor</param> /// <returns>当前Tensor</returns> public TensorOld Add(TensorOld t) { if (t.ElementCount == 1) { return(Add(t.GetValue())); } CheckShape(shape, t.shape); Add(this, t, this); return(this); }
public static void CheckMultipleShape(TensorOld a, TensorOld b) { if (a.Rank != 2 || b.Rank != 2) { throw new NotImplementedException("only suport multiple between scalar, vector and matrix!"); } if (a.shape[1] != b.shape[0]) { throw new TensorShapeException($"can't multiple matrix between {a.shape.ToString()} and {b.shape.ToString()}"); } }
public TensorOld Apply(TensorOld a, Func <double, double, double> function) { Parallel.ForEach(Partitioner.Create(0, values.Length), arg => { for (long i = arg.Item1; i < arg.Item2; i++) { values[i] = function(values[i], a.values[i]); } }); return(this); }
/// <summary> /// 把当前Tensor转置,转置后返回的是一个新的Tensor /// </summary> /// <returns>转置后的Tensor</returns> public TensorOld Transpose() { var result = new TensorOld(shape.Reverse().ToArray()); for (int i = 0; i < ElementCount; i++) { var index = GetIndex(i); var transIndex = index.Reverse().ToArray(); result[transIndex] = values[i]; } return(result); }
public void StartTrain(TensorOld trainX, TensorOld trainY, TensorOld testX, TensorOld testY) { Console.WriteLine("Preparing data..."); SetTrainingData(trainX, trainY, testX, testY); Console.WriteLine("Complete!"); Training = true; epochCounter = 0; batchCounter = 1; startTime = DateTime.Now; var lastSave = false; while (Training && epochCounter < Epoch) { Console.WriteLine($"\n==================== Epoch {epochCounter + 1} ====================\n"); var epochStart = DateTime.Now; TrainEpoch(); ShowTime(epochStart); var trainYHat = Model.Predict(TrainX); LastTrainLoss = Model.GetLoss(TrainY, trainYHat); LastTrainAccuracy = Model.GetAccuracy(TrainY, trainYHat); ShowTrainLossAll(); //test the test data when one Epoch complete if (TestX != null && TestY != null) { var testYHat = Model.Predict(TestX); LastTestLoss = Model.GetLoss(TestY, testYHat); LastTestAccuracy = Model.GetAccuracy(TestY, testYHat); ShowTestLoss(); } if (SaveTrainerEpochs > 0 && epochCounter % SaveTrainerEpochs == 0) { SaveTrainer(); if (Epoch - epochCounter == 1) { lastSave = true; } } epochCounter++; } if (SaveTrainerEpochs > 0 && !lastSave) { SaveTrainer(); } ShowCompelteInfo(); }
public List <string> Predict(TensorOld X) { X = Normalize(X); LastRawResult = Model.Predict(X); LastCodecResult = Utilities.ProbabilityToCode(LastRawResult); if (LabelCodec != null) { return(LabelCodec.Decode(LastCodecResult)); } return(LastRawResult.GetRawValues().Select(a => a.ToString()).ToList()); }
public static double ComputeAccuracy(TensorOld y, TensorOld yHat) { var count = y.shape[0]; var eq = 0d; for (int i = 0; i < count; i++) { if (RowEqual(y, yHat, i * y.shape[1], y.shape[1])) { eq++; } } return(eq / count); }
/// <summary> /// 训练前的准备工作,检查并确定所需Tensor的结构并分配好内存 /// </summary> /// <param name="y">样本标签</param> /// <param name="yHat">输出标签</param> public override void PrepareTrain(TensorOld y, TensorOld yHat) { TensorOld.CheckShape(y, yHat); if (y.Rank != 2) { throw new TensorShapeException("y and yHat must Rank=2"); } ForwardOutput = new TensorOld(y.shape[0]); BackwardOutput = yHat.GetSameShape(); yBuff = new double[y.shape[1]]; yHatBuff = new double[y.shape[1]]; derBuff = new double[y.shape[1]]; sampleNumber = y.Shape[0]; }
private void ComputeCrossEntropy(TensorOld y, TensorOld yHat) { var foreoutData = ForwardOutput.GetRawValues(); for (int i = 0; i < sampleNumber; i++) { //取出一个样本及其对应的Label y.GetByDim1(i, yBuff); yHat.GetByDim1(i, yHatBuff); //计算交叉熵 foreoutData[i] = Functions.CrossEntropy(yBuff, yHatBuff); } Array.Copy(y.values, 0, BackwardOutput.values, 0, y.ElementCount); }
/// <summary> /// a和b对应位置元素执行function操作,结果写入result对应位置,要求a,b,result结构一致 /// 该方法不做结构一致性检查,必要时在调用之前检查。 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="result"></param> /// <param name="function"></param> public static void Apply(TensorOld a, TensorOld b, TensorOld result, Func <double, double, double> function) { //这个方法中不进行Tensor结构一致性的检查 //所有的Tensor结构的问题都放到Prepare过程中 //或者必要的时候在调用这个函数之前执行Tensor结构一致性的检查 Parallel.ForEach(Partitioner.Create(0, result.values.Length), arg => { for (long i = arg.Item1; i < arg.Item2; i++) { result.values[i] = function(a.values[i], b.values[i]); } }); }
/// <summary> /// 两个Tensor的点积,结果返回为新的Tensor,要求两个Tensor结构一致 /// </summary> /// <param name="a">Tensor1</param> /// <param name="b">Tensor2</param> /// <returns>包含结果的新的Tensor</returns> public static TensorOld MultipleElementWise(TensorOld a, TensorOld b) { if (a.ElementCount == 1) { return(Multiple(b, a.GetValue())); } if (b.ElementCount == 1) { return(Multiple(a, b.GetValue())); } var result = a.GetSameShape(); MultipleElementWise(a, b, result); return(result); }
public static bool CheckShapeBool(TensorOld t1, TensorOld t2) { if (t1.shape.Length != t2.shape.Length) { return(false); } for (int i = 0; i < t1.shape.Length; i++) { if (t1.shape[i] != t2.shape[i]) { return(false); } } return(true); }
public override double GetLoss(TensorOld y, TensorOld yHat) { var outData = ForwardOutput.GetRawValues(); var result = 0d; for (int i = 0; i < y.shape[0]; i++) { //取出一个样本及其对应的Label y.GetByDim1(i, yBuff); yHat.GetByDim1(i, yHatBuff); //计算交叉熵 result += Functions.CrossEntropy(yBuff, yHatBuff); } return(result / sampleNumber); }
/// <summary> /// Tensor和Tensor对应元素相加,结果返回为新的Tensor,要求两个Tensor结构相同 /// </summary> /// <param name="a">Tensor</param> /// <param name="b">Tensor</param> /// <returns>相加后的结果</returns> public static TensorOld Add(TensorOld a, TensorOld b) { if (a.ElementCount == 1) { return(Add(b, a.GetValue())); } if (b.ElementCount == 1) { return(Add(a, b.GetValue())); } CheckShape(a, b); var result = a.GetSameShape(); Add(a, b, result); return(result); }
private void SetTrainingData(TensorOld trainX, TensorOld trainY, TensorOld testX, TensorOld testY) { if (Normalizer != null) { TrainX = Normalizer.Normalize(trainX); if (testX != null) { TestX = Normalizer.Normalize(testX); } } else { TrainX = trainX; TestX = testX; } if (LabelCodec != null) { TrainY = LabelCodec.Encode(trainY.GetRawValues()); if (testY != null) { TestY = LabelCodec.Encode(testY.GetRawValues()); } } else { TrainY = trainY; TestY = testY; } var shape = TrainX.Shape; shape[0] = BatchSize; xBuff = new TensorOld(shape); yBuff = new TensorOld(BatchSize, TrainY.shape[1]); sampleCount = TrainX.shape[0]; xWidth = TrainX.ElementCount / TrainX.shape[0]; yWidth = TrainY.ElementCount / TrainY.shape[0]; batchPerEpoch = trainX.shape[0] / BatchSize; if (trainX.shape[0] % BatchSize != 0) { batchPerEpoch++; } }
private void ComputeCrossEntropy(TensorOld y, TensorOld yHat) { var forwardoutData = ForwardOutput.GetRawValues(); var backwardoutData = BackwardOutput.GetRawValues(); for (int i = 0; i < sampleNumber; i++) { //取出一个样本及其对应的Label y.GetByDim1(i, yBuff); yHat.GetByDim1(i, yHatBuff); //计算交叉熵 forwardoutData[i] = Functions.CrossEntropy(yBuff, yHatBuff); //计算损失函数关于输入的导数 Derivatives.CrossEntropy(yBuff, yHatBuff, derBuff); Array.Copy(derBuff, 0, backwardoutData, i * derBuff.Length, derBuff.Length); } }
public static void Multiple(TensorOld a, TensorOld b, TensorOld result) { var rows = a.shape[0]; var cols = b.shape[1]; var bStep = a.shape[1]; Parallel.For(0, rows, i => { var aStart = a.GetRawOffset(i, 0); var resultStart = result.GetRawOffset(i, 0); Parallel.For(0, cols, j => { var sum = 0d; var bStart = b.GetRawOffset(0, j); for (int k = 0; k < bStep; k++) { sum += a.values[aStart + k] * b.values[k * cols + j]; } //result.values[i * cols + j] = sum; result.SetValueFast(sum, i, j); }); }); }
/// <summary> /// 计算Loss和Loss对yHat的导数(梯度) /// </summary> /// <param name="y"></param> /// <param name="yHat"></param> public override void Compute(TensorOld y, TensorOld yHat) { LastY = y; LastYHat = yHat; ComputeCrossEntropy(y, yHat); }
/// <summary> /// a和b相加结果写入result参数 /// 必要的时候在调用这个方法前进行Tensor结构一致性检查 /// </summary> /// <param name="a">加数1</param> /// <param name="b">加数2</param> /// <param name="result">结果</param> public static void Add(TensorOld a, TensorOld b, TensorOld result) { //放弃Tensor结构的检查 Apply(a, b, result, (x, y) => x + y); }
public List <double> Predict(TensorOld X) { X = Normalize(X); LastRawResult = Model.Predict(X); return(LastRawResult.GetRawValues().ToList()); }
public static void Add(TensorOld t, double d, TensorOld result) { Apply(t, result, a => a + d); }
/// <summary> /// 两个Tensor对应元素相除,结果保存在当前Tensor /// 还不知道有什么用 ;) /// </summary> /// <param name="t">除数</param> /// <returns>当前Tensor</returns> public TensorOld DivideElementWise(TensorOld t) { DivideElementWise(this, t, this); return(this); }
/// <summary> /// a每个元素除以b对应元素,结果写入result /// 必要的时候在调用这个方法前进行Tensor结构一致性检查 /// </summary> /// <param name="a">被除数</param> /// <param name="b">除数</param> /// <param name="result">结果</param> public static void DivideElementWise(TensorOld a, TensorOld b, TensorOld result) { Apply(a, b, result, (x, y) => x / y); }
public static void MeanSquareError(TensorOld y, TensorOld yHat, TensorOld result) { //因为存在learning rate,所以梯度前面的系数不那么重要,但最好和损失函数一致, TensorOld.Minus(yHat, y, result); result.Multiple(2d / y.ElementCount); }
/// <summary> /// a和b的点积,结果写入result /// 必要的时候在调用这个方法前进行Tensor结构一致性检查 /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="result"></param> public static void MultipleElementWise(TensorOld a, TensorOld b, TensorOld result) { Apply(a, b, result, (x, y) => x * y); }
/// <summary> /// Tensor每个元素乘上d,结果写入result /// </summary> /// <param name="t">Tensor乘数</param> /// <param name="d">标量乘数</param> /// <param name="result">结果</param> public static void Multiple(TensorOld t, double d, TensorOld result) { Apply(t, result, a => a * d); }
public override double GetAccuracy(TensorOld y, TensorOld yHat) { var code = Utilities.ProbabilityToCode(yHat); return(ComputeAccuracy(y, code)); }