-
Notifications
You must be signed in to change notification settings - Fork 7
A2. Introduction to State Machine
StateMachine is the main component of the RCNet library. Through a simple API, it allows the client application to make predictions and classifications of time series by applying the Reservoir Computing methods. StateMachine internally encapsulates two independent components: NeuralPreprocessor and ReadoutLayer. NeuralPreprocessor contains reservoirs of recurrently interconnected neurons and provides nonlinear transformation of time series data into predictors. Based on these predictors, ReadoutLayer performs forecasting and/or classification through trained non-recurrent networks. How NeuralPreprocessor will transform time series data into predictors, what tasks ReadoutLayer will perform, and how it will perform them, all need to be configured. How to configure StateMachine will be described later in a separate section of the documentation. This section describes the basic interaction of the client application with StateMachine.
State machine has two constructors:
/// <summary>
/// Creates an initialized instance.
/// </summary>
/// <param name="cfg">The state machine configuration.</param>
public StateMachine(StateMachineSettings cfg);
/// <summary>
/// Creates an initialized instance.
/// </summary>
/// <param name="xmlFileName">The name of xml file where the root element matches the state machine configuration.</param>
public StateMachine(string xmlFileName);
To create a StateMachine instance, you must first instantiate the StateMachineSettings class or have the configuration stored in a XML file. Configuring StateMachine can be quite complex, so how to do it will be described later in a separate section of the documentation.
StateMachine instance must be trained before use. It is also good practice to verify the accuracy of the trained instance. StateMachine's methods for training and subsequent verification are:
/// <summary>
/// Performs the training of the state machine.
/// </summary>
/// <param name="trainingData">The training data bundle.</param>
/// <param name="controller">The build process controller (optional).</param>
/// <returns>The training results.</returns>
public TrainingResults Train(VectorBundle trainingData, TNRNetBuilder.BuildControllerDelegate controller = null);
/// <summary>
/// Verifies the state machine's accuracy.
/// </summary>
/// <remarks>
/// Evaluates the computed data against the ideal data.
/// </remarks>
/// <param name="verificationData">The verification data bundle.</param>
/// <returns>The verification results.</returns>
public VerificationResults Verify(VectorBundle verificationData);
The following diagram shows a typical sequence for creating a trained StateMachine instance.
StateMachine's Train and Verify methods require datasets containing vector pairs. Each vector pair consists of an input data vector and a corresponding output data vector. The data is expected in natural form, so it does not have to be modified in any way (normalization, standardization), everything is provided internally by StateMachine. Both methods get a dataset through an instance of the VectorBundle class, where the input and output data vector is represented by double array (double[]). The VectorBundle class has three constructors:
/// <summary>
/// Creates an uninitialized instance.
/// </summary>
public VectorBundle();
/// <summary>
/// Creates an uninitialized instance.
/// </summary>
/// <param name="expectedNumOfPairs">The expected number of vector pairs.</param>
public VectorBundle(int expectedNumOfPairs);
/// <summary>
/// Creates an initialized instance.
/// </summary>
/// <param name="inputVectorCollection">The collection of input vectors.</param>
/// <param name="outputVectorCollection">The collection of output vectors.</param>
public VectorBundle(IEnumerable<double[]> inputVectorCollection, IEnumerable<double[]> outputVectorCollection);
Vector pair can be added into the bundle using the AddPair method. All vector pairs from another VectorBundle instance can be added using the Add method. Methods are defined as follows:
/// <summary>
/// Adds the vector pair into the bundle.
/// </summary>
/// <param name="inputVector">The input vector.</param>
/// <param name="outputVector">The output vector.</param>
public void AddPair(double[] inputVector, double[] outputVector);
/// <summary>
/// Adds all the vector pairs from another vector bundle.
/// </summary>
/// <param name="data">Another vector bundle.</param>
public void Add(VectorBundle data);
VectorBundle class also offers to create its instance directly from a csv data. Class's static methods are:
/// <summary>
/// Loads the vector bundle from the csv data (continuous input feeding).
/// </summary>
/// <param name="csvData">The csv data.</param>
/// <param name="inputFieldNameCollection">The names of input fields.</param>
/// <param name="outputFieldNameCollection">The names of output fields.</param>
/// <param name="remainingInputVector">The last unused input vector.</param>
public static VectorBundle Load(CsvDataHolder csvData,
List<string> inputFieldNameCollection,
List<string> outputFieldNameCollection,
out double[] remainingInputVector
);
/// <summary>
/// Loads the vector bundle from the csv data (patterned input feeding).
/// </summary>
/// <param name="csvData">The csv data.</param>
/// <param name="numOfOutputFields">The number of output fields.</param>
public static VectorBundle Load(CsvDataHolder csvData, int numOfOutputFields);
Both static methods require instance of CsvDataHolder class. You can create a CsvDataHolder instance from a csv file using the following CsvDataHolder class constructor:
/// <summary>
/// Creates an initialized instance.
/// </summary>
/// <param name="fileName">Name of the file containing the data to be loaded.</param>
/// <param name="header">Specifies whether the first row contains the column names.</param>
/// <param name="delimiter">The delimiter of the data items. If CsvDataHolder.AutoDetectDelimiter is specified than delimiter will be recognized automatically.</param>
public CsvDataHolder(string fileName, bool header, char delimiter = AutoDetectDelimiter);
At the top of thinking about the time dependent data processing is the data context. StateMachine can work in two basic modes of input data processing called "Continuous Input Feeding" and "Patterned Input Feeding".
In continuous input mode, one vector pair contains input and expected output data of single time point of the series, so sequence of input vectors within the VectorBundle represents the time series of variable(s) values. Obviously, an order of the vector pairs in VectorBundle instance is important. The state of predictors-generating NeuralPreprocessor component is never reset and is changing every time the next time point of time series is processed. Therefore the continuity of an input must be permanently kept. Continuous input mode is used where it is necessary to keep long context of the time series to calculate proper output. A good example is the prediction of the future price of a stock based on previous changes of its price over time.
In patterned input mode, one vector pair consists of shorter input time series (pattern) and corresponding output data as a result of that pattern. Each vector pair represents an independent case with no relation to other vector pairs within the VectorBundle so an order of the vector pairs within the VectorBundle instance is not important. Patterned input mode is used where it is not necessary to have long context of the time series to calculate proper output. A good example is the recognition of the movement type based on the short isolated sequence of position changes over time.
Training and verification are usually relatively long-lasting operations. StateMachine allows the client application to track progress through regularly generated events.
This event is raised every time an input vector of VectorBundle is preprocessed by the NeuralPreprocessor component.
An event handler has the following prototype:
/// <summary>
/// The delegate of the PreprocessingProgressChanged event handler.
/// </summary>
/// <param name="totalNumOfInputs">The total number of inputs to be processed.</param>
/// <param name="numOfProcessedInputs">The number of already processed inputs.</param>
/// <param name="finalPreprocessingOverview">The final overview of the preprocessing.</param>
public delegate void PreprocessingProgressChangedHandler(int totalNumOfInputs,
int numOfProcessedInputs,
PreprocessingOverview finalPreprocessingOverview
);
A PreprocessingOverview class instance is passed to handler at the end, when all inputs are preprocessed and it contains behavioral statistics of NeuralPreprocessor. It is extremely important to be sure the NeuralPreprocessor instance is working properly. How to read the statistics will be desribed in the detail later.
To make things easier, the PreprocessingOverview class offers to get a textual analytical report through following method:
/// <summary>
/// Builds the text report.
/// </summary>
/// <param name="margin">Specifies the text left margin.</param>
/// <returns>The built text report.</returns>
public string CreateReport(int margin = 0)
ReadoutLayer uses hierarchical structures of trained non-recurrent networks and so there may be necessary to build a relatively large number of non-recurrent end-network instances. This event is raised every time a training epoch of the particular end-network is done.
An event handler has the following prototype:
/// <summary>
/// The delegate of the RLBuildProgressChanged event handler.
/// </summary>
/// <param name="buildProgress">The current state of the build process.</param>
public delegate void RLBuildProgressChangedHandler(BuildProgress buildProgress);
An instance of the BuildProgress class is passed to the handler and contains information about the progress of the ReadoutLayer build, as well as detailed metrics of the currently being built non-recurrent end-network.
BuildProgress class implements the IBuildProgress interface:
/// <summary>
/// Common interface of the build progress information holders.
/// </summary>
public interface IBuildProgress
{
/// <summary>
/// Indicates that the build of a new end-network has started.
/// </summary>
bool NewEndNetwork { get; }
/// <summary>
/// Indicates important progress information that should be reported.
/// </summary>
bool ShouldBeReported { get; }
/// <summary>
/// Gets currently processed end network epoch number.
/// </summary>
int EndNetworkEpochNum { get; }
/// <summary>
/// Gets the textual information about the build progress.
/// </summary>
/// <param name="margin">Left margin (number of spaces).</param>
/// <param name="includeName">Specifies whether to include name of the entity being built in the textual information.</param>
/// <returns>Built text message.</returns>
string GetInfoText(int margin = 0, bool includeName = true);
}//IBuildProgress
A simple handler can look like this:
/// <summary>
/// Displays information about the readout layer build process progress.
/// </summary>
/// <param name="buildProgress">The current state of the build process.</param>
protected void OnRLBuildProgressChanged(ReadoutLayer.BuildProgress buildProgress)
{
int reportEpochsInterval = 5;
//Progress info
if (buildProgress.ShouldBeReported || (buildProgress.EndNetworkEpochNum % reportEpochsInterval == 0))
{
//Build progress report message
string progressText = buildProgress.GetInfoText(4);
//Report the progress
if (buildProgress.NewEndNetwork)
{
Console.WriteLine();
}
Console.Write("\x0d" + progressText + " ");
}
return;
}
This event is raised by the StataMachine every time a vector pair of VectorBundle is processed.
An event handler has the following prototype:
/// <summary>
/// The delegate of VerificationProgressChanged event handler.
/// </summary>
/// <param name="totalNumOfInputs">The total number of inputs to be processed.</param>
/// <param name="numOfProcessedInputs">The number of already processed inputs.</param>
public delegate void VerificationProgressChangedHandler(int totalNumOfInputs, int numOfProcessedInputs);
/// <summary>
/// The delegate of a network build process controller invoked during each training epoch.
/// </summary>
/// <remarks>
/// The goal of the build process is to build a network that gives good results both on the training data and the testing data.
/// BuildProgress object contains the best trained network so far and the current one. The primary purpose of controller is
/// to make decision whether the current network is better than the best network so far.
/// The controller can also tell the build process that it does not make any sense to continue. It can
/// stop the current build attempt or the whole build process.
/// </remarks>
/// <param name="buildProgress">The BuildProgress object containing all the necessary information to control the build process.</param>
/// <returns>Instructions for the network build process.</returns>
public delegate BuildInstr BuildControllerDelegate(BuildProgress buildProgress);
StateMachine is serializable. This allows you to separate the preparation of a trained StateMachine instance from its use in a client application. The methods for serialization and deserialization of StateMachine are:
/// <summary>
/// Serializes this instance into the specified stream.
/// </summary>
/// <param name="stream">The stream to be used.</param>
public void Serialize(Stream stream);
/// <summary>
/// Serializes this instance into the specified file.
/// </summary>
/// <param name="fileName">The name of the file.</param>
public void Serialize(string fileName);
/// <summary>
/// Deserializes the state machine instance from the specified stream.
/// </summary>
/// <param name="stream">The stream to be used.</param>
/// <returns>The instance of the state machine.</returns>
public static StateMachine Deserialize(Stream stream);
/// <summary>
/// Deserializes the state machine instance from the specified file.
/// </summary>
/// <param name="fileName">The name of the file.</param>
/// <returns>The instance of the state machine.</returns>
public static StateMachine Deserialize(string fileName);
The following diagram shows a typical sequence of using a trained StateMachine instance.
- class StateMachine
- class StateMachineSettings
- class VectorBundle
- class NeuralPreprocessor
- class ReadoutLayer
- class TNRNetBuilder
Questions, ideas, suggestions for improvement and constructive comments are welcome at my email address [email protected] or newly you can use github discussions.