Projekt

Obecné

Profil

Stáhnout (14.2 KB) Statistiky
| Větev: | Tag: | Revize:
1 ebe96ca4 Roman Kalivoda
//
2
// Author: Roman Kalivoda
3
//
4
5
using System;
6
using System.Collections.Generic;
7 d358b79e Roman Kalivoda
using ServerApp.Connection.XMLProtocolHandler;
8 ce0940b5 Roman Kalivoda
using ServerApp.Parser.Parsers;
9
using Newtonsoft.Json;
10
using ServerApp.WeatherPredictionParser;
11 870cd163 Roman Kalivoda
using ServerApp.Parser.OutputInfo;
12 0d31f7e0 Roman Kalivoda
using log4net;
13 0e7b6b11 Roman Kalivoda
using System.IO;
14
using System.Text.RegularExpressions;
15
using System.Linq;
16 ebe96ca4 Roman Kalivoda
17
namespace ServerApp.Predictor
18
{
19
    /// <summary>
20
    /// Implentation of the <c>IPredicitionController</c> interface.
21
    /// </summary>
22 ce0940b5 Roman Kalivoda
    public class PredictionController : IPredictionController
23 ebe96ca4 Roman Kalivoda
    {
24 0d31f7e0 Roman Kalivoda
        private static readonly ILog _log = LogManager.GetLogger(typeof(PredictionController));
25
26 0e7b6b11 Roman Kalivoda
        /// <summary>
27
        /// ID of the current predictor instances.
28
        /// </summary>
29
        private string PredictorID;
30
31 ebe96ca4 Roman Kalivoda
        /// <summary>
32 ce0940b5 Roman Kalivoda
        /// Configuration of the <c>Predictor</c>
33 ebe96ca4 Roman Kalivoda
        /// </summary>
34 0e7b6b11 Roman Kalivoda
        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 ebe96ca4 Roman Kalivoda
41 0e7b6b11 Roman Kalivoda
        /// <summary>
42
        /// Current predictor instances
43
        /// </summary>
44
        private IPredictor[] Predictors;
45 ebe96ca4 Roman Kalivoda
46 6dc585b4 Roman Kalivoda
        /// <summary>
47
        /// A mutual exclusive lock to pro
48
        /// </summary>
49
        private readonly object predictorsLock = new Object();
50
51 ebe96ca4 Roman Kalivoda
        /// <summary>
52
        /// A reference to a data parser.
53
        /// </summary>
54 ce0940b5 Roman Kalivoda
        private IDataParser DataParser;
55 ebe96ca4 Roman Kalivoda
56
        /// <summary>
57
        /// A feature extractor instance.
58
        /// </summary>
59 ce0940b5 Roman Kalivoda
        private FeatureExtractor FeatureExtractor;
60
61
        /// <summary>
62
        /// A weather prediction parser service
63
        /// </summary>
64
        private IJsonParser weatherService;
65 870cd163 Roman Kalivoda
66 ebe96ca4 Roman Kalivoda
        /// <summary>
67
        /// Instantiates new prediction controller.
68
        /// </summary>
69
        /// <param name="dataParser">A data parser used to get training data.</param>
70 ce0940b5 Roman Kalivoda
        public PredictionController(IJsonParser weatherService, IDataParser dataParser, string pathToConfig = null)
71 ebe96ca4 Roman Kalivoda
        {
72 0d31f7e0 Roman Kalivoda
            _log.Info("Constructing a new PredictionController instance.");
73 870cd163 Roman Kalivoda
            this.weatherService = weatherService;
74 cdeee9f8 Roman Kalivoda
            // load config or get the default one
75
            if (pathToConfig is null)
76
            {
77
                pathToConfig = PredictorConfiguration.DEFAULT_CONFIG_PATH;
78
            }
79
            try
80
            {
81 0e7b6b11 Roman Kalivoda
                string json = File.ReadAllText(pathToConfig);
82 cdeee9f8 Roman Kalivoda
                this.Configuration = JsonConvert.DeserializeObject<PredictorConfiguration>(json);
83 0d31f7e0 Roman Kalivoda
            }
84 3c4b53fe Roman Kalivoda
            catch (System.IO.FileNotFoundException e)
85 cdeee9f8 Roman Kalivoda
            {
86 29a3f064 Roman Kalivoda
                Console.WriteLine("Warning: could not find a configuration file, creating a new one:");
87
                Console.WriteLine(e.Message.PadLeft(4));
88 cdeee9f8 Roman Kalivoda
                this.Configuration = PredictorConfiguration.GetDefaultConfig();
89
            }
90
91
            this.DataParser = dataParser;
92 6dc585b4 Roman Kalivoda
            var predictors = new IPredictor[this.Configuration.PredictorCount];
93 cdeee9f8 Roman Kalivoda
            this.FeatureExtractor = new FeatureExtractor(this.DataParser, this.Configuration);
94 22211075 Roman Kalivoda
95 3c4b53fe Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
96 76072df0 Roman Kalivoda
            _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 3c4b53fe Roman Kalivoda
            FileInfo[] files = di.GetFiles();
103 76072df0 Roman Kalivoda
            if (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))).Count() == Configuration.PredictorCount)
104 ebe96ca4 Roman Kalivoda
            {
105 3c4b53fe Roman Kalivoda
                _log.Info("Found existing predictors, loading the newest.");
106 76072df0 Roman Kalivoda
                var predictorID = 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))).Last().Select(f => f.Name.Split("_".ToCharArray())[0]).First();
107
                this.Load(predictorID);
108 3c4b53fe Roman Kalivoda
            }
109
            else
110
            {
111
                _log.Info("No predictors found, creating new ones");
112
                for (int i = 0; i < this.Configuration.PredictorCount; i++)
113
                {
114 76072df0 Roman Kalivoda
                    predictors[i] = new SdcaMaximumEntropyClassifier();
115
                }
116
                lock (predictorsLock)
117
                {
118
                    this.Predictors = predictors;
119 3c4b53fe Roman Kalivoda
                }
120 ebe96ca4 Roman Kalivoda
            }
121 76072df0 Roman Kalivoda
            
122 ce0940b5 Roman Kalivoda
            PredictorConfiguration.SaveConfig(PredictorConfiguration.DEFAULT_CONFIG_PATH, Configuration);
123 ebe96ca4 Roman Kalivoda
        }
124
        public List<string> GetPredictors()
125
        {
126 ce0940b5 Roman Kalivoda
            return new List<string>(this.Configuration.BuildingsToAreas.Keys);
127 ebe96ca4 Roman Kalivoda
        }
128
129 3c4b53fe Roman Kalivoda
        public int Load(string predictorID)
130 ebe96ca4 Roman Kalivoda
        {
131 76072df0 Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath); 
132
            if (!di.Exists)
133
            {
134
                _log.Warn("The model data directory could not be found.");
135
                return 2;
136
            }
137 0e7b6b11 Roman Kalivoda
            FileInfo[] files = di.GetFiles($"{predictorID}_*.zip");
138 3c4b53fe Roman Kalivoda
            if (Array.FindAll(files, f => Regex.IsMatch(f.Name, $@"{predictorID}_\d+.zip")).Any()){
139
                IPredictor[] newPredictors = new IPredictor[this.Configuration.PredictorCount];
140
                try
141
                {
142
                    for (int i = 0; i < this.Configuration.PredictorCount; i++)
143
                    {
144 76072df0 Roman Kalivoda
                        newPredictors[i] = new SdcaMaximumEntropyClassifier(Array.Find(files, f => Regex.IsMatch(f.Name, $"{predictorID}_{i}.zip")).FullName);
145 3c4b53fe Roman Kalivoda
                    }
146
                    files = di.GetFiles($"{predictorID}.txt");
147 6dc585b4 Roman Kalivoda
                    var dataFilenames = File.ReadLines(files[0].FullName); 
148
                    lock (predictorsLock)
149
                    {
150
                        this.Predictors = newPredictors;
151
                        this.DataFilenames = dataFilenames;
152
                        this.PredictorID = predictorID;
153
                    }
154 3c4b53fe Roman Kalivoda
                } catch (FileNotFoundException e)
155
                {
156
                    _log.Error(e.ToString());
157
                    return 2;
158
                }
159
            } else
160
            {
161
                _log.Debug("Could not find predictor with given predictorID");
162
                return 1;
163
            }
164
            return 0;
165
        }
166
167
        public void Save()
168
        {
169
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
170 76072df0 Roman Kalivoda
            if (!di.Exists)
171
            {
172
                _log.Warn("The model data directory could not be found.");
173
                return;
174
            }
175 3c4b53fe Roman Kalivoda
176 6dc585b4 Roman Kalivoda
            lock (predictorsLock)
177 ebe96ca4 Roman Kalivoda
            {
178 6dc585b4 Roman Kalivoda
                for (int i = 0; i < this.Configuration.PredictorCount; i++)
179
                {
180
                    Predictors[i].Save(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
181
                }
182
                File.WriteAllLinesAsync(Path.Combine(di.FullName, $"{PredictorID}.txt"), this.DataFilenames);
183 ebe96ca4 Roman Kalivoda
            }
184 0e7b6b11 Roman Kalivoda
        }
185
186 3c4b53fe Roman Kalivoda
        public int Rollback()
187 0e7b6b11 Roman Kalivoda
        {
188 3c4b53fe Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
189 76072df0 Roman Kalivoda
            if (!di.Exists)
190
            {
191
                _log.Warn("The model data directory could not be found.");
192
                return 2;
193
            }
194 3c4b53fe Roman Kalivoda
            FileInfo[] files = di.GetFiles();
195 76072df0 Roman Kalivoda
            if (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))).Count() == Configuration.PredictorCount)
196 3c4b53fe Roman Kalivoda
            {
197
                string RollbackedPredictorID = 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))).Last().Select(f => f.Name.Split("_".ToCharArray())[0]).First();
198
                this.Delete(this.PredictorID);
199
                return this.Load(RollbackedPredictorID);
200
            } else
201
            {
202
                // indicate that older model does not exist
203
                return 1;
204
            }
205
        }
206
207
        private void Delete(string predictorID)
208
        {
209
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
210 76072df0 Roman Kalivoda
            if (!di.Exists)
211
            {
212
                _log.Warn("The model data directory could not be found.");
213
                return;
214
            }
215 0e7b6b11 Roman Kalivoda
216 3c4b53fe Roman Kalivoda
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
217
            {
218
                File.Delete(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
219
            }
220
            File.Delete(Path.Combine(di.FullName, $"{PredictorID}.txt"));
221 ebe96ca4 Roman Kalivoda
        }
222
223 d358b79e Roman Kalivoda
        public Response Predict(Request request)
224 ebe96ca4 Roman Kalivoda
        {
225 0d31f7e0 Roman Kalivoda
            _log.Info($"Received a prediction request: endDate={request.useEndDate}, weather={request.useWeather}");
226 870cd163 Roman Kalivoda
            DateTime start = new DateTime(year: request.start.year, month: request.start.month, day: request.start.day, hour: request.start.hour, minute: 0, second: 0);
227
            List<Prediction> predictions = new List<Prediction>();
228
            if (request.useEndDate)
229
            {
230
                DateTime end = new DateTime(year: request.end.year, month: request.end.month, day: request.end.day, hour: request.end.hour, minute: 0, second: 0);
231
                DateTime current = start;
232
                while (current < end)
233
                {
234 0d31f7e0 Roman Kalivoda
                    _log.Debug($"Predicting for date {current.Date.ToShortDateString()}");
235 870cd163 Roman Kalivoda
                    while (current.Hour < Date.MAX_HOUR)
236
                    {
237 0d31f7e0 Roman Kalivoda
                        _log.Debug($"Predicting for time {current.TimeOfDay.ToString()}");
238 870cd163 Roman Kalivoda
                        var prediction = PredictSingle(request, current);
239
                        predictions.Add(prediction);
240
                        current = current.AddHours(this.Configuration.TimeResolution);
241
                    }
242
                    current = current.AddHours(23 - current.Hour + Date.MIN_HOUR);
243
                }
244 0d31f7e0 Roman Kalivoda
            }
245
            else
246 870cd163 Roman Kalivoda
            {
247 0d31f7e0 Roman Kalivoda
                _log.Debug("Predicting for single DateTime.");
248
                predictions.Add(PredictSingle(request, start));
249 870cd163 Roman Kalivoda
            }
250
            var response = new Response();
251
            response.hoursPerSegment = Configuration.TimeResolution;
252
            response.predicitons = predictions.ToArray();
253 0d31f7e0 Roman Kalivoda
            _log.Debug($"Created a response.");
254 870cd163 Roman Kalivoda
            return response;
255 ebe96ca4 Roman Kalivoda
        }
256
257 0060a0ae Roman Kalivoda
        private Prediction PredictSingle(Request request, DateTime predictionTime)
258 870cd163 Roman Kalivoda
        {
259
            double[] predictedValues = new double[this.Configuration.BuildingsToAreas.Count];
260 0e7b6b11 Roman Kalivoda
            string[] predictedLabels = new string[this.Predictors.Length];
261
            for (int i = 0; i < this.Predictors.Length; i++)
262 870cd163 Roman Kalivoda
            {
263
                if (request.useWeather)
264
                {
265 0d31f7e0 Roman Kalivoda
                    _log.Debug("Predicting for requested weather.");
266 6dc585b4 Roman Kalivoda
                    lock (predictorsLock)
267 870cd163 Roman Kalivoda
                    {
268 6dc585b4 Roman Kalivoda
                        predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
269
                        {
270
                            Rain = (float)request.rain,
271
                            Temp = (float)request.temperature,
272
                            Wind = (float)request.wind,
273
                            Hour = predictionTime.Hour,
274
                            Time = predictionTime
275
                        });
276
                    }
277 870cd163 Roman Kalivoda
                }
278
                else
279
                {
280 0d31f7e0 Roman Kalivoda
                    _log.Debug("Retrieving weather info from the weather service.");
281
                    weatherService.ParsePrediction();
282 0060a0ae Roman Kalivoda
                    WeatherInfo weatherInfo = weatherService.Predictions.Find(info => info.startTime.Date.Equals(predictionTime.Date) && predictionTime.TimeOfDay.Subtract(info.startTime.TimeOfDay).Hours < info.intervalLength);
283 1f1235a8 Roman Kalivoda
                    if (weatherInfo is null)
284 870cd163 Roman Kalivoda
                    {
285 1f1235a8 Roman Kalivoda
                        predictedLabels[i] = null;
286
                    }
287
                    else
288
                    {
289 6dc585b4 Roman Kalivoda
                        lock (predictorsLock)
290 1f1235a8 Roman Kalivoda
                        {
291 6dc585b4 Roman Kalivoda
                            predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
292
                            {
293
                                Rain = weatherInfo.rain,
294
                                Temp = (float)weatherInfo.temp,
295
                                Wind = (float)weatherInfo.wind,
296
                                Hour = predictionTime.Hour,
297
                                Time = predictionTime
298
                            });
299
                        }
300 1f1235a8 Roman Kalivoda
                    }
301 870cd163 Roman Kalivoda
                }
302
            }
303
            for (int i = 0; i < predictedValues.Length; i++)
304
            {
305 1f1235a8 Roman Kalivoda
                predictedValues[i] = this.FeatureExtractor.LabelToRatio(predictedLabels[this.Configuration.BuildingsToAreas[TagInfo.buildings[i]]]);
306 870cd163 Roman Kalivoda
            }
307
308
            Prediction prediction = new Prediction();
309
            prediction.dateTime = new Date
310
            {
311 0060a0ae Roman Kalivoda
                year = predictionTime.Year,
312
                month = predictionTime.Month,
313
                day = predictionTime.Day,
314
                hour = predictionTime.Hour
315 870cd163 Roman Kalivoda
            };
316
            prediction.predictions = predictedValues;
317 0d31f7e0 Roman Kalivoda
            _log.Debug($"Created prediction for DateTime: {prediction.dateTime}");
318 870cd163 Roman Kalivoda
            return prediction;
319
        }
320 ebe96ca4 Roman Kalivoda
321 0e7b6b11 Roman Kalivoda
        public void Train()
322 ebe96ca4 Roman Kalivoda
        {
323 0e7b6b11 Roman Kalivoda
            DataParser.Parse(DateTime.MinValue, DateTime.MaxValue, this.Configuration.TimeResolution, wholeDay: false);
324 6dc585b4 Roman Kalivoda
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
325 3c4b53fe Roman Kalivoda
            {
326
                // train on all available data
327
                List<ModelInput> data = FeatureExtractor.PrepareTrainingInput(i);
328
                Console.WriteLine("Training predictor with {0} samples.", data.Count);
329 6dc585b4 Roman Kalivoda
                lock (predictorsLock)
330
                {
331
                    this.Predictors[i].Fit(data);
332
                }
333
            }
334
            lock (predictorsLock)
335
            {
336
                this.DataFilenames = this.DataParser.WeatherDataUsed.Concat(this.DataParser.ActivityDataUsed);
337
                this.PredictorID = DateTime.Now.ToBinary().ToString();
338 3c4b53fe Roman Kalivoda
            }
339
            this.Save();
340 ebe96ca4 Roman Kalivoda
        }
341
342 0e7b6b11 Roman Kalivoda
        public IEnumerable<string> GetDataFileNames()
343
        {
344
            return this.DataFilenames;
345
        }
346 ebe96ca4 Roman Kalivoda
    }
347
}