Projekt

Obecné

Profil

Stáhnout (14.3 KB) Statistiky
| Větev: | Tag: | Revize:
1
//
2
// Author: Roman Kalivoda
3
//
4

    
5
using System;
6
using System.Collections.Generic;
7
using ServerApp.Connection.XMLProtocolHandler;
8
using ServerApp.Parser.Parsers;
9
using Newtonsoft.Json;
10
using ServerApp.WeatherPredictionParser;
11
using ServerApp.Parser.OutputInfo;
12
using log4net;
13
using System.IO;
14
using System.Text.RegularExpressions;
15
using System.Linq;
16

    
17
namespace ServerApp.Predictor
18
{
19
    /// <summary>
20
    /// Implentation of the <c>IPredicitionController</c> interface.
21
    /// </summary>
22
    public class PredictionController : IPredictionController
23
    {
24
        private static readonly ILog _log = LogManager.GetLogger(typeof(PredictionController));
25

    
26
        /// <summary>
27
        /// ID of the current predictor instances.
28
        /// </summary>
29
        private string PredictorID;
30

    
31
        /// <summary>
32
        /// Configuration of the <c>Predictor</c>
33
        /// </summary>
34
        public PredictorConfiguration Configuration { get; set; }
35

    
36
        /// <summary>
37
        /// Names of the files used to train the current predictor instances.
38
        /// </summary>
39
        private IEnumerable<string> DataFilenames;
40

    
41
        /// <summary>
42
        /// Current predictor instances
43
        /// </summary>
44
        private IPredictor[] Predictors;
45

    
46
        /// <summary>
47
        /// A mutual exclusive lock to pro
48
        /// </summary>
49
        private readonly object predictorsLock = new Object();
50

    
51
        /// <summary>
52
        /// A reference to a data parser.
53
        /// </summary>
54
        private IDataParser DataParser;
55

    
56
        /// <summary>
57
        /// A feature extractor instance.
58
        /// </summary>
59
        private FeatureExtractor FeatureExtractor;
60

    
61
        /// <summary>
62
        /// A weather prediction parser service
63
        /// </summary>
64
        private IJsonParser weatherService;
65

    
66
        /// <summary>
67
        /// Instantiates new prediction controller.
68
        /// </summary>
69
        /// <param name="dataParser">A data parser used to get training data.</param>
70
        public PredictionController(IJsonParser weatherService, IDataParser dataParser, string pathToConfig = null)
71
        {
72
            _log.Info("Constructing a new PredictionController instance.");
73
            this.weatherService = weatherService;
74
            // load config or get the default one
75
            if (pathToConfig is null)
76
            {
77
                pathToConfig = PredictorConfiguration.DEFAULT_CONFIG_PATH;
78
            }
79
            try
80
            {
81
                string json = File.ReadAllText(pathToConfig);
82
                this.Configuration = JsonConvert.DeserializeObject<PredictorConfiguration>(json);
83
            }
84
            catch (System.IO.FileNotFoundException e)
85
            {
86
                Console.WriteLine("Warning: could not find a configuration file, creating a new one:");
87
                Console.WriteLine(e.Message.PadLeft(4));
88
                this.Configuration = PredictorConfiguration.GetDefaultConfig();
89
            }
90

    
91
            this.DataParser = dataParser;
92
            var predictors = new IPredictor[this.Configuration.PredictorCount];
93
            this.FeatureExtractor = new FeatureExtractor(this.DataParser, this.Configuration);
94

    
95
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
96
            _log.Debug($"Looking for directory: {Configuration.ModelDataPath}");
97
            if (! di.Exists)
98
            {
99
                _log.Info($"Creating the model data directory: {Configuration.ModelDataPath}");
100
                di.Create();
101
            }
102
            FileInfo[] files = di.GetFiles();
103
            var orderedEnumerable = Array.FindAll(files, f => Regex.IsMatch(f.Name, @"[-]?\d+_\d+.zip")).GroupBy(f => f.Name.Split("_.".ToCharArray())[0]).OrderBy(f => DateTime.FromBinary(Convert.ToInt64(f.Key)));
104
            if (orderedEnumerable.Where(g => g.Count() == Configuration.PredictorCount).Any())
105
            {
106
                _log.Info("Found existing predictors, loading the newest.");
107
                var predictorID = orderedEnumerable.Last().Select(f => f.Name.Split("_".ToCharArray())[0]).First();
108
                this.Load(predictorID);
109
            }
110
            else
111
            {
112
                _log.Info("No predictors found, creating new ones");
113
                for (int i = 0; i < this.Configuration.PredictorCount; i++)
114
                {
115
                    predictors[i] = new SdcaMaximumEntropyClassifier();
116
                }
117
                lock (predictorsLock)
118
                {
119
                    this.Predictors = predictors;
120
                }
121
                this.Train();
122
            }
123
            
124
            PredictorConfiguration.SaveConfig(PredictorConfiguration.DEFAULT_CONFIG_PATH, Configuration);
125
        }
126
        public List<string> GetPredictors()
127
        {
128
            return new List<string>(this.Configuration.BuildingsToAreas.Keys);
129
        }
130

    
131
        public int Load(string predictorID)
132
        {
133
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath); 
134
            if (!di.Exists)
135
            {
136
                _log.Warn("The model data directory could not be found.");
137
                return 2;
138
            }
139
            FileInfo[] files = di.GetFiles($"{predictorID}_*.zip");
140
            if (Array.FindAll(files, f => Regex.IsMatch(f.Name, $@"{predictorID}_\d+.zip")).Any()){
141
                IPredictor[] newPredictors = new IPredictor[this.Configuration.PredictorCount];
142
                try
143
                {
144
                    for (int i = 0; i < this.Configuration.PredictorCount; i++)
145
                    {
146
                        newPredictors[i] = new SdcaMaximumEntropyClassifier(Array.Find(files, f => Regex.IsMatch(f.Name, $"{predictorID}_{i}.zip")).FullName);
147
                    }
148
                    files = di.GetFiles($"{predictorID}.txt");
149
                    var dataFilenames = File.ReadLines(files[0].FullName); 
150
                    lock (predictorsLock)
151
                    {
152
                        this.Predictors = newPredictors;
153
                        this.DataFilenames = dataFilenames;
154
                        this.PredictorID = predictorID;
155
                    }
156
                } catch (FileNotFoundException e)
157
                {
158
                    _log.Error(e.ToString());
159
                    return 2;
160
                }
161
            } else
162
            {
163
                _log.Debug("Could not find predictor with given predictorID");
164
                return 1;
165
            }
166
            return 0;
167
        }
168

    
169
        public void Save()
170
        {
171
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
172
            if (!di.Exists)
173
            {
174
                _log.Warn("The model data directory could not be found.");
175
                return;
176
            }
177

    
178
            lock (predictorsLock)
179
            {
180
                for (int i = 0; i < this.Configuration.PredictorCount; i++)
181
                {
182
                    Predictors[i].Save(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
183
                }
184
                File.WriteAllLinesAsync(Path.Combine(di.FullName, $"{PredictorID}.txt"), this.DataFilenames);
185
            }
186
        }
187

    
188
        public int Rollback()
189
        {
190
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
191
            if (!di.Exists)
192
            {
193
                _log.Warn("The model data directory could not be found.");
194
                return 2;
195
            }
196
            FileInfo[] files = di.GetFiles();
197
            var orderedEnumerable = Array.FindAll(files, f => Regex.IsMatch(f.Name, @"[-]?\d+_\d+.zip")).GroupBy(f => f.Name.Split("_.".ToCharArray())[0]).OrderBy(f => DateTime.FromBinary(Convert.ToInt64(f.Key))).Where(f => ! f.Key.Equals(this.PredictorID));
198
            if (orderedEnumerable.Where(g => g.Count() == Configuration.PredictorCount).Any())
199
            {
200
                string RollbackedPredictorID = orderedEnumerable.Last().Select(f => f.Name.Split("_".ToCharArray())[0]).First();
201
                this.Delete(this.PredictorID);
202
                return this.Load(RollbackedPredictorID);
203
            } else
204
            {
205
                // indicate that older model does not exist
206
                return 1;
207
            }
208
        }
209

    
210
        private void Delete(string predictorID)
211
        {
212
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
213
            if (!di.Exists)
214
            {
215
                _log.Warn("The model data directory could not be found.");
216
                return;
217
            }
218

    
219
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
220
            {
221
                File.Delete(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
222
            }
223
            File.Delete(Path.Combine(di.FullName, $"{PredictorID}.txt"));
224
        }
225

    
226
        public Response Predict(Request request)
227
        {
228
            _log.Info($"Received a prediction request: endDate={request.useEndDate}, weather={request.useWeather}");
229
            DateTime start = new DateTime(year: request.start.year, month: request.start.month, day: request.start.day, hour: request.start.hour, minute: 0, second: 0);
230
            List<Prediction> predictions = new List<Prediction>();
231
            if (request.useEndDate)
232
            {
233
                DateTime end = new DateTime(year: request.end.year, month: request.end.month, day: request.end.day, hour: request.end.hour, minute: 0, second: 0);
234
                DateTime current = start;
235
                while (current <= end)
236
                {
237
                    _log.Debug($"Predicting for date {current.Date.ToShortDateString()}");
238
                    while (current.Hour <= Date.MAX_HOUR)
239
                    {
240
                        if (current.Date == end.Date && current.Hour > end.Hour)
241
                        {
242
                            break;  // break if the end date was reached and hour is after the end time.
243
                        }
244
                        _log.Debug($"Predicting for time {current.TimeOfDay.ToString()}");
245
                        var prediction = PredictSingle(request, current);
246
                        predictions.Add(prediction);
247
                        current = current.AddHours(this.Configuration.TimeResolution);
248
                    }
249
                    current = current.AddHours(23 - current.Hour + Date.MIN_HOUR);
250
                }
251
            }
252
            else
253
            {
254
                _log.Debug("Predicting for single DateTime.");
255
                predictions.Add(PredictSingle(request, start));
256
            }
257
            var response = new Response();
258
            response.hoursPerSegment = Configuration.TimeResolution;
259
            response.predicitons = predictions.ToArray();
260
            _log.Debug($"Created a response.");
261
            return response;
262
        }
263

    
264
        private Prediction PredictSingle(Request request, DateTime predictionTime)
265
        {
266
            double[] predictedValues = new double[this.Configuration.BuildingsToAreas.Count];
267
            string[] predictedLabels = new string[this.Predictors.Length];
268
            for (int i = 0; i < this.Predictors.Length; i++)
269
            {
270
                if (request.useWeather)
271
                {
272
                    _log.Debug("Predicting for requested weather.");
273
                    lock (predictorsLock)
274
                    {
275
                        predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
276
                        {
277
                            Rain = (float)request.rain,
278
                            Temp = (float)request.temperature,
279
                            Wind = (float)request.wind,
280
                            Hour = predictionTime.Hour,
281
                            Time = predictionTime
282
                        });
283
                    }
284
                }
285
                else
286
                {
287
                    _log.Debug("Retrieving weather info from the weather service.");
288
                    weatherService.ParsePrediction();
289
                    WeatherInfo weatherInfo = weatherService.Predictions.Find(info => info.startTime.Date.Equals(predictionTime.Date) && predictionTime.TimeOfDay.Subtract(info.startTime.TimeOfDay).Hours < info.intervalLength);
290
                    if (weatherInfo is null)
291
                    {
292
                        predictedLabels[i] = null;
293
                    }
294
                    else
295
                    {
296
                        lock (predictorsLock)
297
                        {
298
                            predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
299
                            {
300
                                Rain = weatherInfo.rain,
301
                                Temp = (float)weatherInfo.temp,
302
                                Wind = (float)weatherInfo.wind,
303
                                Hour = predictionTime.Hour,
304
                                Time = predictionTime
305
                            });
306
                        }
307
                    }
308
                }
309
            }
310
            for (int i = 0; i < predictedValues.Length; i++)
311
            {
312
                predictedValues[i] = this.FeatureExtractor.LabelToRatio(predictedLabels[this.Configuration.BuildingsToAreas[TagInfo.buildings[i]]]);
313
            }
314

    
315
            Prediction prediction = new Prediction();
316
            prediction.dateTime = new Date
317
            {
318
                year = predictionTime.Year,
319
                month = predictionTime.Month,
320
                day = predictionTime.Day,
321
                hour = predictionTime.Hour
322
            };
323
            prediction.predictions = predictedValues;
324
            _log.Debug($"Created prediction for DateTime: {prediction.dateTime}");
325
            return prediction;
326
        }
327

    
328
        public void Train()
329
        {
330
            DataParser.Parse(DateTime.MinValue, DateTime.MaxValue, this.Configuration.TimeResolution, wholeDay: false);
331
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
332
            {
333
                // train on all available data
334
                List<ModelInput> data = FeatureExtractor.PrepareTrainingInput(i);
335
                Console.WriteLine("Training predictor with {0} samples.", data.Count);
336
                lock (predictorsLock)
337
                {
338
                    this.Predictors[i].Fit(data);
339
                }
340
            }
341
            lock (predictorsLock)
342
            {
343
                this.DataFilenames = this.DataParser.WeatherDataUsed.Concat(this.DataParser.ActivityDataUsed);
344
                this.PredictorID = DateTime.Now.ToBinary().ToString();
345
            }
346
            this.Save();
347
        }
348

    
349
        public IEnumerable<string> GetDataFileNames()
350
        {
351
            return this.DataFilenames;
352
        }
353
    }
354
}
(9-9/11)