Page 26 - MSDN Magazine, December 15, 2017
P. 26
The other expected inputs (lstm_1_h_in and lstm_1_c_in of shape [128, 1, 1]) are more surprising, but “h” is often used for an LSTM’s output and “C” for the cell’s state. Setting all the values for these inputs to 0 results in correct predictions, so that’s what TideInput does.
Call the inferencing function. With everything configured, it’s time for the magic! The Android call inferenceInterface.Run(new string[] { OUTPUT_VARIABLE_NAME });, and the iOS call, var prediction = model.GetPrediction(inputs, out mlErr);, perform the actual inferencing. In both cases, this is a synchronous call. Timing, of course, varies from machine to machine and model to model, and is very quick on the small tide-prediction model. With image-recognition models, though, I’ve seen this function take many hundreds of milliseconds. When writing apps that work with video frames, I’ve used simple background processing and a simple flag to throttle requests to the ML library. In iOS 11.1, Apple added new Pressure Level APIs for its video subsystem that can interrupt capture sessions if the hardware gets too hot, which supports the intuition that things like continuous image recog- nition and augmented reality are pretty darn processor-intense!
Figure 10 Configuring CoreML Input
Retrieve the output variable or variables. Just as with the inputs, the outputs of the model are associated with strings. In the case of Android:
float[] predictions = new float[OUTPUT_SIZE]; inferenceInterface.Fetch(OUTPUT_VARIABLE_NAME, predictions);
These populate the predictions array, while the iOS code is only slightly more complex:
var predictionMultiArray = prediction.GetFeatureValue(OUTPUT_VARIABLE_NAME).MultiArrayValue;
var predictedLevels = new float[OUTPUT_SIZE]; for (int i = 0; i < OUTPUT_SIZE; i++)
{
predictedLevels[i] = predictionMultiArray[i].FloatValue; }
Both CoreML and TAI are performance-optimized libraries that do little or no input validation. Data is basically treated as raw C buffers; mistakes in input size, shape or format can result in diagnostic-free crashes or, even more confusingly, complaint-free “Garbage In, Garbage Out” results.
The field of ML is developing at a blistering pace, and it would be a full-time job just to evaluate the latest research papers and industry announcements. While the cutting edge is fascinating, huge amounts of value can be unlocked using techniques and architectures that have been around practically forever (that is, years).
There is a rough progression of difficulty in ML tasks from pattern recognition to classification to sequence modeling to sequence-to-sequence generation. Of course, the signal needs to be present in the data, and recognizing a pattern in a very noisy stream may be more difficult than generating something that acts like a simple Markov model (I’m looking at you, “Neural Net Generates Funny Boat Names!” articles).
Keep It Simple
My ML models are almost exclusively done with high-level abstrac- tions and well-known architectures. Even though I can rarely resist pre-processing my data to emphasize necessary features, this often turns out to be premature optimization. It’s amazing how often “the simplest architecture that could possibly work” manages to extract a signal from unprocessed data. Lord Kelvin’s tide computer relied on the results of a Fourier analysis of water levels, but the simple model I developed reproduces at least the short-term epicycles pretty well. (I plan on seeing if I can generate the classic harmonic components from raw data—stay tuned to my Twitter feed.)
Whether your ambitions involve super-human competence, in-the-field business intelligence, or simply boosting your career prospects, modern ML is a fascinating field with powerful capa- bilities and seemingly unlimited potential for delivering value. Delivering that ML capability on devices is straightforward with Microsoft’s Xamarin technologies and the techniques described in this article. n
Larry O’Brien is a senior content publication manager for Microsoft’s Xamarin technologies. His first published technical article was “Developing a Neural Network in C++” for AI Expert in 1989. He’s seen ’em come and he’s seen ’em go. He’s on Twitter: @lobrien.
Thanks to the following Microsoft technical experts for reviewing this article: Anuj Bhatia and Alexander Kyte
class TideInput : NSObject, IMLFeatureProvider {
MLFeatureValue readings;
MLMultiArray lstm_1_h_in, lstm_1_c_in; const int INPUT_SIZE = 200;
const int MIDDLE_SIZE = 128;
public NSSet<NSString> FeatureNames {
get {
return new NSSet<NSString>( new NSString("readings"), new NSString("lstm_1_h_in"), new NSString("lstm_1_c_in")
); }
}
public MLFeatureValue GetFeatureValue(string featureName) {
switch (featureName) {
case "readings": return readings;
case "lstm_1_h_in": return MLFeatureValue.Create(lstm_1_h_in); case "lstm_1_c_in": return MLFeatureValue.Create(lstm_1_c_in); default: throw new ArgumentOutOfRangeException();
} }
public TideInput(float[] tideInputData) {
// 200 elements, 1 batch, 1 feature
NSError mlErr;
var ma = new MLMultiArray(new nint[] { INPUT_SIZE, 1, 1 },
MLMultiArrayDataType.Double, out mlErr); for (int i = 0; i < INPUT_SIZE; i++)
{
ma[i] = tideInputData[i]; }
readings = MLFeatureValue.Create(ma);
lstm_1_h_in = new MLMultiArray(new nint[] { MIDDLE_SIZE },
MLMultiArrayDataType.Double, out mlErr);
lstm_1_c_in = new MLMultiArray(new nint[] { MIDDLE_SIZE },
MLMultiArrayDataType.Double, out mlErr); for (int i = 0; i < MIDDLE_SIZE; i++)
{
lstm_1_h_in[i] = lstm_1_c_in[i] = new NSNumber(0.0); }
} }
22 msdn magazine
Machine Learning