ml.py 9.25 KB
Newer Older
Chetan Sharma's avatar
Chetan Sharma committed
1
2
3
4
import abc
import numpy as np

from sklearn import linear_model
Chetan Sharma's avatar
Chetan Sharma committed
5
from scipy import stats
Chetan Sharma's avatar
Chetan Sharma committed
6
7
from matplotlib import pyplot as plt

Chetan Sharma's avatar
Chetan Sharma committed
8
from models import T_lin, F_lin, T_lin_full, F_lin_full, T_x_vector, T_x_vector_padded, Fy_x_vector, T_x_vector_full, Fy_x_vector_full
Chetan Sharma's avatar
Chetan Sharma committed
9
from objects import Data, Conditions, EndMill, Prediction
Chetan Sharma's avatar
Chetan Sharma committed
10

Chetan Sharma's avatar
Chetan Sharma committed
11
# https://stackoverflow.com/questions/11686720
Chetan Sharma's avatar
Chetan Sharma committed
12
13


Chetan Sharma's avatar
Chetan Sharma committed
14
15
16
17
18
def mean_no_outliers(data, m=2):
    d = np.abs(data - np.median(data))
    mdev = np.median(d)
    s = d / (mdev if mdev else 1.)
    return np.mean(data[s < m])
Chetan Sharma's avatar
Chetan Sharma committed
19

Chetan Sharma's avatar
Chetan Sharma committed
20

Chetan Sharma's avatar
Chetan Sharma committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Model(abc.ABC):
    """
    Represents a model that can be trained with one training datum at a time.
    """
    @abc.abstractmethod
    def ingest_datum(self, datum):
        """
        Ingests one datum.
        Args:
            datum : A Data object.
        """
        pass

    def ingest_data(self, data):
35
36
        for datum in data:
            self.ingest_datum(datum)
Chetan Sharma's avatar
Chetan Sharma committed
37
38
39
40
41

    @abc.abstractmethod
    def predict_one(self, conditions):
        """
        Predicts milling forces using the model as it currently is.
Chetan Sharma's avatar
Chetan Sharma committed
42
43
44
45
46
        Args:
            conditions: a single condition object

        Return:
            A Prediction using the model in its current state
Chetan Sharma's avatar
Chetan Sharma committed
47
48
49
50
        """
        pass

    def predict(self, conditions):
51
        return list(map(self.predict_one, conditions))
Chetan Sharma's avatar
Chetan Sharma committed
52
53


Chetan Sharma's avatar
Chetan Sharma committed
54
55
class UnifiedLinearModel(Model):
    """A model that uses a simple linear regressor.
Chetan Sharma's avatar
Chetan Sharma committed
56

Chetan Sharma's avatar
Chetan Sharma committed
57
58
59
    Args:
        initial_params: Initial parameters to use with this model. Helps with bootstrapping.
    """
60

Chetan Sharma's avatar
Chetan Sharma committed
61
    def __init__(self, initial_params=[0, 0, 0, 0]):
62
63
64
65
66
67
        self.training_Tx = list()
        self.training_Ty = list()
        self.training_Fyx = list()
        self.training_Fyy = list()

        self.regressor = linear_model.LinearRegression(fit_intercept=False)
Chetan Sharma's avatar
Chetan Sharma committed
68
        self.params = initial_params
69
70
71
72

    def ingest_datum(self, datum):
        # decompose
        _, _, _, _, _, Ts, Fys = datum.unpack()
Chetan Sharma's avatar
Chetan Sharma committed
73
74
75
        # mean filter + rejection of outliers
        # T, Fy = np.median(Ts[:, 1]), np.median(Fys[:, 1])
        T, Fy = mean_no_outliers(Ts[:, 1]), mean_no_outliers(Fys[:, 1])
76
77
78
79
        # get linear coefficients
        T_x = np.array(T_x_vector_padded(datum.conditions()))
        Fy_x = np.array(Fy_x_vector(datum.conditions()))

Chetan Sharma's avatar
Chetan Sharma committed
80
81
82
83
84
85
86
        # normalizing independently while preserving ratios
        norm_T = np.linalg.norm(T_x)
        T_x /= norm_T
        T /= norm_T
        norm_Fy = np.linalg.norm(Fy_x)
        Fy_x /= norm_Fy
        Fy /= norm_Fy
87
88
89
90
91
92
93
94
95
96
97
98
99

        # add T to training set
        self.training_Tx.append(T_x)
        self.training_Ty.append(T)

        # add Fy to training set
        self.training_Fyx.append(Fy_x)
        self.training_Fyy.append(Fy)

        self.update()

    def update(self):
        # calculate best fit from data
Chetan Sharma's avatar
Chetan Sharma committed
100
101
        self.regressor.fit(self.training_Tx + self.training_Fyx,
                           self.training_Ty + self.training_Fyy)
102
103
104
105
        self.params = np.array(self.regressor.coef_)

    def predict_one(self, conditions):
        # evaluate
Chetan Sharma's avatar
Chetan Sharma committed
106
        T = T_lin(conditions, *self.params)
107
108
109
110
111
        F = F_lin(conditions, *self.params)

        # repack and return
        return Prediction(*conditions.unpack(), T, F)

Chetan Sharma's avatar
Chetan Sharma committed
112
113
114
115
116
    def score(self):
        return self.regressor.score(self.training_Tx + self.training_Fyx, self.training_Ty + self.training_Fyy)


class UnifiedLinearModelFull(Model):
Chetan Sharma's avatar
Chetan Sharma committed
117
118
119
120
    """Same as linear model, but also tries to compensate for variation of cutting pressures with cutting speed. Doesn't really work.
    """

    def __init__(self, initial_params=[0, 0, 0, 0, 0, 0]):
Chetan Sharma's avatar
Chetan Sharma committed
121
122
123
124
125
126
127
128
129
130
131
132
133
        self.training_Tx = list()
        self.training_Ty = list()
        self.training_Fyx = list()
        self.training_Fyy = list()

        self.regressor = linear_model.LinearRegression(fit_intercept=False)
        self.params = initial_params

    def ingest_datum(self, datum):
        # decompose
        _, _, _, _, _, Ts, Fys = datum.unpack()
        # mean filter + rejection of outliers
        # T, Fy = np.median(Ts[:, 1]), np.median(Fys[:, 1])
Chetan Sharma's avatar
Chetan Sharma committed
134
        T, Fy = mean_no_outliers(Ts[:, 1]), mean_no_outliers(Fys[:, 1])
Chetan Sharma's avatar
Chetan Sharma committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
        # get linear coefficients
        T_x = np.array(T_x_vector_full(datum.conditions()))
        Fy_x = np.array(Fy_x_vector_full(datum.conditions()))

        # normalizing independently while preserving ratios
        norm_T = np.linalg.norm(T_x)
        T_x /= norm_T
        T /= norm_T
        norm_Fy = np.linalg.norm(Fy_x)
        Fy_x /= norm_Fy
        Fy /= norm_Fy

        # add T to training set
        self.training_Tx.append(T_x)
        self.training_Ty.append(T)

        # add Fy to training set
        self.training_Fyx.append(Fy_x)
        self.training_Fyy.append(Fy)

        self.update()

    def update(self):
        # calculate best fit from data
Chetan Sharma's avatar
Chetan Sharma committed
159
160
        self.regressor.fit(self.training_Tx + self.training_Fyx,
                           self.training_Ty + self.training_Fyy)
Chetan Sharma's avatar
Chetan Sharma committed
161
162
163
164
165
166
167
168
169
170
        self.params = np.array(self.regressor.coef_)

    def predict_one(self, conditions):
        # evaluate
        T = T_lin_full(conditions, *self.params)
        F = F_lin_full(conditions, *self.params)

        # repack and return
        return Prediction(*conditions.unpack(), T, F)

Chetan Sharma's avatar
Chetan Sharma committed
171

172
class RANSACLinearModel(Model):
Chetan Sharma's avatar
Chetan Sharma committed
173
174
175
176
    """Same as linear model, but also uses RANSAC. Doesn't really work all that well.
    """

    def __init__(self, initial_params=[0, 0, 0, 0]):
177
178
179
180
181
182
        self.training_Tx = list()
        self.training_Ty = list()
        self.training_Fyx = list()
        self.training_Fyy = list()

        base_regressor = linear_model.LinearRegression(fit_intercept=False)
Chetan Sharma's avatar
Chetan Sharma committed
183
184
        self.regressor = linear_model.RANSACRegressor(
            base_regressor, min_samples=5)
Chetan Sharma's avatar
Chetan Sharma committed
185
        self.params = initial_params
186
187
188
189

    def ingest_datum(self, datum):
        # decompose
        _, _, _, _, _, Ts, Fys = datum.unpack()
Chetan Sharma's avatar
Chetan Sharma committed
190
        T, Fy = mean_no_outliers(Ts[:, 1]), mean_no_outliers(Fys[:, 1])
191
192
193
194
195
        # get linear coefficients
        T_x = np.array(T_x_vector_padded(datum.conditions()))
        Fy_x = np.array(Fy_x_vector(datum.conditions()))

        # we want to artificially inflate T to be as big as F
Chetan Sharma's avatar
Chetan Sharma committed
196
197
198
199
200
201
        norm_T = np.linalg.norm(T_x)
        T_x /= norm_T
        T /= norm_T
        norm_Fy = np.linalg.norm(Fy_x)
        Fy_x /= norm_Fy
        Fy /= norm_Fy
202
203
204
205
206
207
208
209
210
211
212
213
214
215

        # add T to training set
        self.training_Tx.append(T_x)
        self.training_Ty.append(T)

        # add Fy to training set
        self.training_Fyx.append(Fy_x)
        self.training_Fyy.append(Fy)

        if (len(self.training_Tx) + len(self.training_Fyx)) > 4:
            self.update()

    def update(self):
        # calculate best fit from data
Chetan Sharma's avatar
Chetan Sharma committed
216
217
        self.regressor.fit(self.training_Tx + self.training_Fyx,
                           self.training_Ty + self.training_Fyy)
218
219
220
221
        self.params = np.array(self.regressor.estimator_.coef_)

    def predict_one(self, conditions):
        # evaluate
Chetan Sharma's avatar
Chetan Sharma committed
222
223
224
225
226
227
        T = T_lin(conditions, *self.params)
        F = F_lin(conditions, *self.params)

        # repack and return
        return Prediction(*conditions.unpack(), T, F)

Chetan Sharma's avatar
Chetan Sharma committed
228

Chetan Sharma's avatar
Chetan Sharma committed
229
230
231
232
class BayesianLinearModel(Model):
    """
    Utilizes a Bayesian approach to pick "safe" values for the coefficients.
    That is, we have a confidence interval for our parameters. We pick the upper end for
Chetan Sharma's avatar
Chetan Sharma committed
233
234
        each coefficient so we don't make too aggressive of a cut.
        (also doesn't really work)
Chetan Sharma's avatar
Chetan Sharma committed
235
    """
Chetan Sharma's avatar
Chetan Sharma committed
236
237

    def __init__(self, initial_params=[0, 0, 0, 0], percentile=.90):
Chetan Sharma's avatar
Chetan Sharma committed
238
239
240
241
242
        self.training_Tx = list()
        self.training_Ty = list()
        self.training_Fyx = list()
        self.training_Fyy = list()

Chetan Sharma's avatar
Chetan Sharma committed
243
244
        self.regressor = linear_model.ARDRegression(
            fit_intercept=False, tol=1e-6, alpha_1=1e-9, alpha_2=1e-9, lambda_1=1e-9, lambda_2=1e-9)
Chetan Sharma's avatar
Chetan Sharma committed
245
246
247
248
249
250
        self.params = initial_params
        self.zscore = stats.norm.ppf(percentile)

    def ingest_datum(self, datum):
        # decompose
        _, _, _, _, _, Ts, Fys = datum.unpack()
Chetan Sharma's avatar
Chetan Sharma committed
251
        T, Fy = mean_no_outliers(Ts[:, 1]), mean_no_outliers(Fys[:, 1])
Chetan Sharma's avatar
Chetan Sharma committed
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
        # get linear coefficients
        T_x = np.array(T_x_vector_padded(datum.conditions()))
        Fy_x = np.array(Fy_x_vector(datum.conditions()))

        # normalize while preserving ratio between x and y
        norm_T = np.linalg.norm(T_x)
        T_x /= norm_T
        T /= norm_T
        norm_Fy = np.linalg.norm(Fy_x)
        Fy_x /= norm_Fy
        Fy /= norm_Fy

        # add T to training set
        self.training_Tx.append(T_x)
        self.training_Ty.append(T)

        # add Fy to training set
        self.training_Fyx.append(Fy_x)
        self.training_Fyy.append(Fy)

        self.update()

    def update(self):
        # calculate best fit from data
Chetan Sharma's avatar
Chetan Sharma committed
276
277
        self.regressor.fit(self.training_Tx + self.training_Fyx,
                           self.training_Ty + self.training_Fyy)
Chetan Sharma's avatar
Chetan Sharma committed
278
279
280
281
282
283
284
285
286
287
288
289
290

        # get params and variance matrix, convert to std deviation
        param_mean = np.array(self.regressor.coef_)
        param_stdv = np.sqrt(np.diag(self.regressor.sigma_))
        print("Param mean: ", param_mean)
        print("param stdv: ", param_stdv)

        # set params to the lower end of our confidence interval, but make sure they're above 0
        self.params = param_mean

    def predict_one(self, conditions):
        # evaluate
        T = T_lin(conditions, *self.params)
291
292
293
294
        F = F_lin(conditions, *self.params)

        # repack and return
        return Prediction(*conditions.unpack(), T, F)