Projekt

Obecné

Profil

Stáhnout (12.4 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
        /// <summary>
47
        /// A reference to a data parser.
48
        /// </summary>
49 ce0940b5 Roman Kalivoda
        private IDataParser DataParser;
50 ebe96ca4 Roman Kalivoda
51
        /// <summary>
52
        /// A feature extractor instance.
53
        /// </summary>
54 ce0940b5 Roman Kalivoda
        private FeatureExtractor FeatureExtractor;
55
56
        /// <summary>
57
        /// A weather prediction parser service
58
        /// </summary>
59
        private IJsonParser weatherService;
60 870cd163 Roman Kalivoda
61 ebe96ca4 Roman Kalivoda
        /// <summary>
62
        /// Instantiates new prediction controller.
63
        /// </summary>
64
        /// <param name="dataParser">A data parser used to get training data.</param>
65 ce0940b5 Roman Kalivoda
        public PredictionController(IJsonParser weatherService, IDataParser dataParser, string pathToConfig = null)
66 ebe96ca4 Roman Kalivoda
        {
67 0e7b6b11 Roman Kalivoda
            // TODO look for existing predictors
68 0d31f7e0 Roman Kalivoda
            _log.Info("Constructing a new PredictionController instance.");
69 870cd163 Roman Kalivoda
            this.weatherService = weatherService;
70 cdeee9f8 Roman Kalivoda
            // load config or get the default one
71
            if (pathToConfig is null)
72
            {
73
                pathToConfig = PredictorConfiguration.DEFAULT_CONFIG_PATH;
74
            }
75
            try
76
            {
77 0e7b6b11 Roman Kalivoda
                string json = File.ReadAllText(pathToConfig);
78 cdeee9f8 Roman Kalivoda
                this.Configuration = JsonConvert.DeserializeObject<PredictorConfiguration>(json);
79 0d31f7e0 Roman Kalivoda
            }
80 3c4b53fe Roman Kalivoda
            catch (System.IO.FileNotFoundException e)
81 cdeee9f8 Roman Kalivoda
            {
82 29a3f064 Roman Kalivoda
                Console.WriteLine("Warning: could not find a configuration file, creating a new one:");
83
                Console.WriteLine(e.Message.PadLeft(4));
84 cdeee9f8 Roman Kalivoda
                this.Configuration = PredictorConfiguration.GetDefaultConfig();
85
            }
86
87
            this.DataParser = dataParser;
88 0e7b6b11 Roman Kalivoda
            this.Predictors = new IPredictor[this.Configuration.PredictorCount];
89 cdeee9f8 Roman Kalivoda
            this.FeatureExtractor = new FeatureExtractor(this.DataParser, this.Configuration);
90 22211075 Roman Kalivoda
91 3c4b53fe Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
92
            FileInfo[] files = di.GetFiles();
93
            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))).Any())
94 ebe96ca4 Roman Kalivoda
            {
95 3c4b53fe Roman Kalivoda
                _log.Info("Found existing predictors, loading the newest.");
96
                this.Load(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());
97
            }
98
            else
99
            {
100
                _log.Info("No predictors found, creating new ones");
101
                for (int i = 0; i < this.Configuration.PredictorCount; i++)
102
                {
103
                    Predictors[i] = new NaiveBayesClassifier();
104
                }
105 ebe96ca4 Roman Kalivoda
            }
106 ce0940b5 Roman Kalivoda
            PredictorConfiguration.SaveConfig(PredictorConfiguration.DEFAULT_CONFIG_PATH, Configuration);
107 ebe96ca4 Roman Kalivoda
        }
108
        public List<string> GetPredictors()
109
        {
110 ce0940b5 Roman Kalivoda
            return new List<string>(this.Configuration.BuildingsToAreas.Keys);
111 ebe96ca4 Roman Kalivoda
        }
112
113 3c4b53fe Roman Kalivoda
        public int Load(string predictorID)
114 ebe96ca4 Roman Kalivoda
        {
115 0e7b6b11 Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
116
            FileInfo[] files = di.GetFiles($"{predictorID}_*.zip");
117 3c4b53fe Roman Kalivoda
            if (Array.FindAll(files, f => Regex.IsMatch(f.Name, $@"{predictorID}_\d+.zip")).Any()){
118
                IPredictor[] newPredictors = new IPredictor[this.Configuration.PredictorCount];
119
                try
120
                {
121
                    for (int i = 0; i < this.Configuration.PredictorCount; i++)
122
                    {
123
                        newPredictors[i] = new NaiveBayesClassifier(Array.Find(files, f => Regex.IsMatch(f.Name, $"{predictorID}_{i}.zip")).FullName);
124
                    }
125
                    this.Predictors = newPredictors;
126
                    files = di.GetFiles($"{predictorID}.txt");
127
                    this.DataFilenames = File.ReadLines(files[0].FullName);
128
                    this.PredictorID = predictorID;
129
                } catch (FileNotFoundException e)
130
                {
131
                    _log.Error(e.ToString());
132
                    return 2;
133
                }
134
            } else
135
            {
136
                // TODO indicate exception
137
                _log.Debug("Could not find predictor with given predictorID");
138
                return 1;
139
            }
140
            return 0;
141
        }
142
143
        public void Save()
144
        {
145
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
146
147 0e7b6b11 Roman Kalivoda
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
148 ebe96ca4 Roman Kalivoda
            {
149 3c4b53fe Roman Kalivoda
                Predictors[i].Save(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
150 ebe96ca4 Roman Kalivoda
            }
151 3c4b53fe Roman Kalivoda
            File.WriteAllLinesAsync(Path.Combine(di.FullName, $"{PredictorID}.txt"), this.DataFilenames);
152 0e7b6b11 Roman Kalivoda
        }
153
154 3c4b53fe Roman Kalivoda
        public int Rollback()
155 0e7b6b11 Roman Kalivoda
        {
156 3c4b53fe Roman Kalivoda
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
157
            FileInfo[] files = di.GetFiles();
158
            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))).Any())
159
            {
160
                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();
161
                this.Delete(this.PredictorID);
162
                return this.Load(RollbackedPredictorID);
163
            } else
164
            {
165
                // indicate that older model does not exist
166
                return 1;
167
            }
168
        }
169
170
        private void Delete(string predictorID)
171
        {
172
            DirectoryInfo di = new DirectoryInfo(Configuration.ModelDataPath);
173 0e7b6b11 Roman Kalivoda
174 3c4b53fe Roman Kalivoda
            for (int i = 0; i < this.Configuration.PredictorCount; i++)
175
            {
176
                File.Delete(Path.Combine(di.FullName, $"{PredictorID}_{i}.zip"));
177
            }
178
            File.Delete(Path.Combine(di.FullName, $"{PredictorID}.txt"));
179 ebe96ca4 Roman Kalivoda
        }
180
181 d358b79e Roman Kalivoda
        public Response Predict(Request request)
182 ebe96ca4 Roman Kalivoda
        {
183 0d31f7e0 Roman Kalivoda
            _log.Info($"Received a prediction request: endDate={request.useEndDate}, weather={request.useWeather}");
184 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);
185
            List<Prediction> predictions = new List<Prediction>();
186
            if (request.useEndDate)
187
            {
188
                DateTime end = new DateTime(year: request.end.year, month: request.end.month, day: request.end.day, hour: request.end.hour, minute: 0, second: 0);
189
                DateTime current = start;
190
                while (current < end)
191
                {
192 0d31f7e0 Roman Kalivoda
                    _log.Debug($"Predicting for date {current.Date.ToShortDateString()}");
193 870cd163 Roman Kalivoda
                    while (current.Hour < Date.MAX_HOUR)
194
                    {
195 0d31f7e0 Roman Kalivoda
                        _log.Debug($"Predicting for time {current.TimeOfDay.ToString()}");
196 870cd163 Roman Kalivoda
                        var prediction = PredictSingle(request, current);
197
                        predictions.Add(prediction);
198
                        current = current.AddHours(this.Configuration.TimeResolution);
199
                    }
200
                    current = current.AddHours(23 - current.Hour + Date.MIN_HOUR);
201
                }
202 0d31f7e0 Roman Kalivoda
            }
203
            else
204 870cd163 Roman Kalivoda
            {
205 0d31f7e0 Roman Kalivoda
                _log.Debug("Predicting for single DateTime.");
206
                predictions.Add(PredictSingle(request, start));
207 870cd163 Roman Kalivoda
            }
208
            var response = new Response();
209
            response.hoursPerSegment = Configuration.TimeResolution;
210
            response.predicitons = predictions.ToArray();
211 0d31f7e0 Roman Kalivoda
            _log.Debug($"Created a response.");
212 870cd163 Roman Kalivoda
            return response;
213 ebe96ca4 Roman Kalivoda
        }
214
215 0060a0ae Roman Kalivoda
        private Prediction PredictSingle(Request request, DateTime predictionTime)
216 870cd163 Roman Kalivoda
        {
217
            double[] predictedValues = new double[this.Configuration.BuildingsToAreas.Count];
218 0e7b6b11 Roman Kalivoda
            string[] predictedLabels = new string[this.Predictors.Length];
219
            for (int i = 0; i < this.Predictors.Length; i++)
220 870cd163 Roman Kalivoda
            {
221
                if (request.useWeather)
222
                {
223 0d31f7e0 Roman Kalivoda
                    _log.Debug("Predicting for requested weather.");
224 870cd163 Roman Kalivoda
                    predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
225
                    {
226
                        Rain = (float)request.rain,
227
                        Temp = (float)request.temperature,
228
                        Wind = (float)request.wind,
229 0060a0ae Roman Kalivoda
                        Hour = predictionTime.Hour,
230
                        Time = predictionTime
231 870cd163 Roman Kalivoda
                    });
232
                }
233
                else
234
                {
235 0d31f7e0 Roman Kalivoda
                    _log.Debug("Retrieving weather info from the weather service.");
236
                    weatherService.ParsePrediction();
237 0060a0ae Roman Kalivoda
                    WeatherInfo weatherInfo = weatherService.Predictions.Find(info => info.startTime.Date.Equals(predictionTime.Date) && predictionTime.TimeOfDay.Subtract(info.startTime.TimeOfDay).Hours < info.intervalLength);
238 1f1235a8 Roman Kalivoda
                    if (weatherInfo is null)
239 870cd163 Roman Kalivoda
                    {
240 1f1235a8 Roman Kalivoda
                        predictedLabels[i] = null;
241
                    }
242
                    else
243
                    {
244
                        predictedLabels[i] = this.Predictors[i].Predict(new ModelInput
245
                        {
246
                            Rain = weatherInfo.rain,
247
                            Temp = (float)weatherInfo.temp,
248
                            Wind = (float)weatherInfo.wind,
249 0060a0ae Roman Kalivoda
                            Hour = predictionTime.Hour,
250
                            Time = predictionTime
251 1f1235a8 Roman Kalivoda
                        });
252
                    }
253 870cd163 Roman Kalivoda
                }
254
            }
255
            for (int i = 0; i < predictedValues.Length; i++)
256
            {
257 1f1235a8 Roman Kalivoda
                predictedValues[i] = this.FeatureExtractor.LabelToRatio(predictedLabels[this.Configuration.BuildingsToAreas[TagInfo.buildings[i]]]);
258 870cd163 Roman Kalivoda
            }
259
260
            Prediction prediction = new Prediction();
261
            prediction.dateTime = new Date
262
            {
263 0060a0ae Roman Kalivoda
                year = predictionTime.Year,
264
                month = predictionTime.Month,
265
                day = predictionTime.Day,
266
                hour = predictionTime.Hour
267 870cd163 Roman Kalivoda
            };
268
            prediction.predictions = predictedValues;
269 0d31f7e0 Roman Kalivoda
            _log.Debug($"Created prediction for DateTime: {prediction.dateTime}");
270 870cd163 Roman Kalivoda
            return prediction;
271
        }
272 ebe96ca4 Roman Kalivoda
273 0e7b6b11 Roman Kalivoda
        public void Train()
274 ebe96ca4 Roman Kalivoda
        {
275 0e7b6b11 Roman Kalivoda
            DataParser.Parse(DateTime.MinValue, DateTime.MaxValue, this.Configuration.TimeResolution, wholeDay: false);
276 3c4b53fe Roman Kalivoda
            for (int i = 0; i < this.Predictors.Length; i++)
277
            {
278
                // train on all available data
279
                List<ModelInput> data = FeatureExtractor.PrepareTrainingInput(i);
280
                Console.WriteLine("Training predictor with {0} samples.", data.Count);
281
                this.Predictors[i].Fit(data);
282
            }
283
            this.DataFilenames = this.DataParser.WeatherDataUsed.Concat(this.DataParser.ActivityDataUsed);
284
            this.PredictorID = DateTime.Now.ToBinary().ToString();
285
            this.Save();
286 ebe96ca4 Roman Kalivoda
        }
287
288 0e7b6b11 Roman Kalivoda
        public IEnumerable<string> GetDataFileNames()
289
        {
290
            return this.DataFilenames;
291
        }
292 ebe96ca4 Roman Kalivoda
    }
293
}