/// <summary> /// Copy constructor. /// </summary> /// <param name="original">the NNetUnit object to be copied</param> /// public NNetUnit(NNetUnit original) { mInput = original.mInput; mSlope = original.mSlope; mAmplify = original.mAmplify; mActivationType = original.mActivationType; }
/// <summary> /// Calculates the error signal on each individual unit in the output layer. /// /// Uses the gradient descent method to search the error surface. /// </summary> /// <param name="nNet">the network undergoing training</param> /// <param name="outErr">the calculated output unit errors</param> /// <param name="response">the network response values</param> /// <param name="nTarget">the index of the corresponding target values in the training set</param> /// private void CalcOutputError(NeuralNet nNet, List <double> outErr, List <double> response, int nTarget) { List <double> unitInputs = new List <double>(); List <double> targetVec = mTrainTarget[nTarget]; // get the output layer activation unit details ActiveT outType = nNet.OutputUnitType; double outSlope = nNet.OutputUnitSlope; double outAmplify = nNet.OutputUnitAmplify; // get the output layer activation unit input values nNet.GetUnitInputs(unitInputs, nNet.NumLayers); for (int i = 0; i < response.Count; i++) { double yi = response[i]; double xi = unitInputs[i]; // follow the steepest path on the error function by moving along the gradient // of the output units activation function - the gradient descent method double err = (targetVec[i] - yi) * GetGradient(outType, outSlope, outAmplify, xi); outErr.Add(err); } }
/// <summary> /// Populates the form's Activation function list boxes with the available /// values of the ActiveT enumerated type. /// </summary> /// private void PopulateActivationListBoxes() { int id = 0; bool done = false; while (!done) { ActiveT func = (ActiveT)id; string funcID = func.ToString(); if (funcID == id.ToString()) { // we have processed all the valid ActiveT enum values done = true; } else { funcID = funcID.Substring(1); this.OutFuncListBox.Items.Add(funcID); this.HidFuncListBox.Items.Add(funcID); id++; } } // select the first entries in the list boxes this.OutFuncListBox.SelectedIndex = 0; this.HidFuncListBox.SelectedIndex = 0; }
/// <summary> /// Calculates the error signal on each individual unit within the networks hidden layers. /// /// Uses the gradient descent method to search the error surface. /// </summary> /// <param name="hidErr">the calculated hidden unit errors</param> /// <param name="outErr">the output unit errors</param> /// <param name="nNet">the network undergoing training</param> /// private void CalcHiddenError(List <List <double> > hidErr, List <double> outErr, NeuralNet nNet) { int nHidden = nNet.NumLayers; double slope = 1, amplify = 1; ActiveT unitType = ActiveT.kUnipolar; // initialise the the previous layer error with the output layer errors List <double> prevErr = new List <double>(); prevErr.AddRange(outErr); // start with the last hidden layer and work back to the first for (int i = nHidden; i >= 1; i--) { List <double> unitInputs = new List <double>(); List <double> layerErr = new List <double>(); // get the weighted connections for the current hidden layer NNetWeightedConnect wtConnect = nNet.GetWeightedConnect(i); int nUnits = wtConnect.NumInputNodes; int nConnect = wtConnect.NumOutputNodes; // get the hidden layer activation unit details nNet.GetLayerDetails(i - 1, ref unitType, ref slope, ref amplify); // get the hidden layer activation unit input values nNet.GetUnitInputs(unitInputs, i - 1); // calculate the hidden layer errors for (int j = 0; j < nUnits; j++) { double error = 0; double xj = unitInputs[j]; for (int k = 0; k < nConnect; k++) { List <double> weights = new List <double>(); wtConnect.GetWeightVector(k, weights); // follow the steepest path on the error function by moving along the gradient // of the hidden layer units activation function - the gradient descent method error += GetGradient(unitType, slope, amplify, xj) * prevErr[k] * weights[j]; } layerErr.Add(error); } // update the hidden errors with the current layer error // N.B. Since we start from the last hidden layer the // hidden layer error signals are stored in reverse order hidErr.Add(layerErr); // back propagate the layer errors prevErr.Clear(); prevErr = layerErr; } }
/// <summary> /// Gets the details of the specified hidden layer. /// </summary> /// <param name="n">the specified hidden layer index</param> /// <param name="unitType">the layer unit activation function type</param> /// <param name="slope">the layer unit activation function slope value</param> /// <param name="amplify">the layer unit activation function amplify value</param> /// public void GetLayerDetails(int n, ref ActiveT unitType, ref double slope, ref double amplify) { if (n >= 0 && n < mNumLayers) { unitType = mActiveUnits[n]; slope = mActiveSlope[n]; amplify = mActiveAmplify[n]; } }
/// <summary> /// Constructs a neuron with the given activation function and settings. /// </summary> /// <param name="activationMode">the unit activation function type</param> /// <param name="slope">the slope parameter value</param> /// <param name="amplify">the amplify parameter value</param> /// public NNetUnit(ActiveT activationMode, double slope, double amplify) { mActivationType = activationMode; // ignore invalid values if (slope > 0) { mSlope = slope; } // ignore invalid values if (amplify > 0) { mAmplify = amplify; } }
///////////////////////////////////////////////////////////////////// // Public Methods ///////////////////////////////////////////////////////////////////// /// <summary> /// Clears a NeuralNetwork object ready for re-use. /// </summary> /// public void ClearNeuralNetwork() { mNumInputs = 0; NumOutputs = 0; mNumLayers = 0; mOutUnitType = ActiveT.kThreshold; mOutUnitSlope = 1; mOutUnitAmplify = 1; mLayers.Clear(); mActivations.Clear(); mUnitInputs.Clear(); mActiveUnits.Clear(); mActiveSlope.Clear(); mActiveAmplify.Clear(); }
/// <summary> /// Gets the gradient of the activation function at the given value of x. /// </summary> /// <param name="unitType">the activation function type</param> /// <param name="slope">the activation function slope value</param> /// <param name="amplify">the activation function amplify value</param> /// <param name="x">calculates the gradient at this value</param> /// <returns>the gradient of the activation function at the given value</returns> /// private double GetGradient(ActiveT unitType, double slope, double amplify, double x) { double gradient = 0; double expMX, expMX1, tanMX, absMX1, grad; switch (unitType) { // 0 everywhere except the origin where the derivative is undefined! case ActiveT.kThreshold: // return the value of the slope parameter if x = 0 if (x == 0) { gradient = slope; } break; case ActiveT.kUnipolar: expMX = Math.Exp(-slope * x); expMX1 = 1 + expMX; gradient = (slope * expMX) / (expMX1 * expMX1); break; case ActiveT.kBipolar: expMX = Math.Exp(-slope * x); expMX1 = 1 + expMX; gradient = (2 * slope * expMX) / (expMX1 * expMX1); break; case ActiveT.kTanh: tanMX = Math.Tanh(slope * x); gradient = slope * (1 - (tanMX * tanMX)); break; case ActiveT.kGauss: gradient = -2 * slope * x * Math.Exp(-slope * x * x); break; case ActiveT.kArctan: gradient = slope / (1 + slope * slope * x * x); break; case ActiveT.kSin: gradient = slope * Math.Cos(slope * x); break; case ActiveT.kCos: gradient = -slope *Math.Sin(slope *x); break; case ActiveT.kSinC: if (Math.Abs(x) < 0.00001) { gradient = 0; } else { gradient = (slope * x * Math.Cos(slope * x) - Math.Sin(slope * x)) / (slope * x * x); } break; case ActiveT.kElliot: absMX1 = 1 + Math.Abs(slope * x); gradient = (0.5 * slope) / (absMX1 * absMX1); break; case ActiveT.kLinear: gradient = slope; break; case ActiveT.kISRU: grad = 1 / Math.Sqrt(1 + slope * x * x); gradient = grad * grad * grad; break; case ActiveT.kSoftSign: absMX1 = 1 + Math.Abs(slope * x); gradient = slope / (absMX1 * absMX1); break; case ActiveT.kSoftPlus: expMX = Math.Exp(slope * x); gradient = (slope * expMX) / (1 + expMX); break; } return(amplify * gradient); }
/// <summary> /// Instantiates this network from a string representation stored in a file. /// </summary> /// <param name="fname">the file containing a string representation of the network</param> /// private void InitializeFromFile(string fname) { StreamReader inStream = new StreamReader(fname); if (inStream != null) { int outUnitType; // deserialize the main details string sNumInputs = ReadString(inStream); string sNumOutputs = ReadString(inStream); string sNumLayers = ReadString(inStream); string sOutUnitType = ReadString(inStream); string sOutUnitSlope = ReadString(inStream); string sOutUnitAmplify = ReadString(inStream); mNumInputs = Convert.ToInt32(sNumInputs); mNumOutputs = Convert.ToInt32(sNumOutputs); mNumLayers = Convert.ToInt32(sNumLayers); outUnitType = Convert.ToInt32(sOutUnitType); mOutUnitSlope = Convert.ToDouble(sOutUnitSlope); mOutUnitAmplify = Convert.ToDouble(sOutUnitAmplify); mOutUnitType = (ActiveT)outUnitType; // deserialize the layer data for (int i = 0; i <= mNumLayers; i++) // use <= to include the output layer { string sDelim = ReadString(inStream); string sNIn = ReadString(inStream); string sNOut = ReadString(inStream); string sNUnit = ReadString(inStream); string sSUnit = ReadString(inStream); string sAUnit = ReadString(inStream); int nIn = Convert.ToInt32(sNIn); int nOut = Convert.ToInt32(sNOut); int nUnit = Convert.ToInt32(sNUnit); double sUnit = Convert.ToDouble(sSUnit); double aUnit = Convert.ToDouble(sAUnit); NNetWeightedConnect connect = new NNetWeightedConnect(nIn, nOut); for (int j = 0; j < nOut; j++) { List <double> weights = new List <double>(); for (int k = 0; k < nIn; k++) { string sWgt = ReadString(inStream); double wgt = Convert.ToDouble(sWgt); weights.Add(wgt); } connect.SetWeightVector(j, weights); } mLayers.Add(connect); mActiveUnits.Add((ActiveT)nUnit); mActiveSlope.Add(sUnit); mActiveAmplify.Add(aUnit); } // tidy up inStream.Close(); } }
/// <summary> /// Adds a new hidden layer. /// /// The hidden layers are stored in the order of calls to this method /// so the first call to AddLayer creates the first hidden layer, the /// second call creates the second layer and so on. /// </summary> /// <param name="numUnits">the number of units in the hidden layer</param> /// <param name="unitType">the layer unit activation function type (defaults to unipolar)</param> /// <param name="initRange">the range of the initial weighted connection values (defaults to 2 coressponding to -1 to +1)</param> /// <param name="slope">the layer unit activation function slope value (defaults to 1.0)</param> /// <param name="amplify">the layer unit activation function amplify value (defaults to 1.0)</param> /// <returns>0 if the layer is successfully added otherwise -1</returns> /// public int AddLayer(int numUnits, ActiveT unitType = ActiveT.kUnipolar, double initRange = 2, double slope = 1, double amplify = 1) { NNetWeightedConnect connect = new NNetWeightedConnect(); NNetWeightedConnect output = new NNetWeightedConnect(); // ignore invalid values if (numUnits > 0 && initRange > 0 && slope > 0 && amplify > 0) { if (mNumLayers == 0) { // configure the first hidden layer if (mNumInputs > 0) { // set up the weighted connections between the input and the first layer // the weighted connections are initialised with random values in the // range: -(initRange / 2) to +(initRange / 2) connect.SetNumNodes(mNumInputs, numUnits, initRange); // store the unit type for the layer mActiveUnits.Add(unitType); // store the steepness of the activation function's slope mActiveSlope.Add(slope); // store the amplification factor of the activation function mActiveAmplify.Add(amplify); mNumLayers++; } else { return(-1); } } else { // configure subsequent hidden layers if (mNumLayers > 0) { int nInputs = mLayers[mNumLayers - 1].NumOutputNodes; // set up the weighted connections between the previous layer and the new one // the weighted connections are initialised with random values in the // range: -(initRange / 2) to +(initRange / 2) connect.SetNumNodes(nInputs, numUnits, initRange); // store the unit type for the layer mActiveUnits.Add(unitType); // store the steepness of the activation function's slope mActiveSlope.Add(slope); // store the amplification factor of the activation function mActiveAmplify.Add(amplify); mNumLayers++; } else { return(-1); } } // connect the last hidden layer to the output layer if (mNumLayers > 1) { // overwrite the old output connections mLayers[mNumLayers - 1] = connect; } else { // add the connections for the first layer mLayers.Add(connect); } // set up the weighted connections between the last layer and the output output.SetNumNodes(numUnits, mNumOutputs, initRange); // add the output connections mLayers.Add(output); } else { return(-1); } return(0); }
/// <summary> /// Copies the contents of the source neural network into this neural network. /// </summary> /// <param name="src">the source net, to be copied</param> /// public void CopyNeuralNet(NeuralNet src) { mLayers.Clear(); mActivations.Clear(); mUnitInputs.Clear(); mActiveUnits.Clear(); mActiveSlope.Clear(); mActiveAmplify.Clear(); mNumInputs = src.mNumInputs; mNumOutputs = src.mNumOutputs; mNumLayers = src.mNumLayers; mOutUnitType = src.mOutUnitType; mOutUnitSlope = src.mOutUnitSlope; mOutUnitAmplify = src.mOutUnitAmplify; // the weighted connections linking the network layers for (int i = 0; i < src.mLayers.Count; i++) { NNetWeightedConnect wCnct = new NNetWeightedConnect(src.mLayers[i]); mLayers.Add(wCnct); } // the activation values for each of the network layers int rowLen = src.mActivations.Count; for (int row = 0; row < rowLen; row++) { List <double> vec = new List <double>(); int colLen = src.mActivations[row].Count; for (int col = 0; col < colLen; col++) { vec.Add(src.mActivations[row][col]); } mActivations.Add(vec); } // the input values for the layer activation functions rowLen = src.mUnitInputs.Count; for (int row = 0; row < rowLen; row++) { List <double> vec = new List <double>(); int colLen = src.mUnitInputs[row].Count; for (int col = 0; col < colLen; col++) { vec.Add(src.mUnitInputs[row][col]); } mUnitInputs.Add(vec); } // the hidden layer unit activation function types for (int i = 0; i < src.mActiveUnits.Count; i++) { mActiveUnits.Add(src.mActiveUnits[i]); } // the hidden layer unit activation function slope values for (int i = 0; i < src.mActiveSlope.Count; i++) { mActiveSlope.Add(src.mActiveSlope[i]); } // the hidden layer unit activation function amplify values for (int i = 0; i < src.mActiveAmplify.Count; i++) { mActiveAmplify.Add(src.mActiveAmplify[i]); } }