Skip to content

Commit 5e050cf

Browse files
author
Jordan Stomps
committed
changing fresh_start methods of models to use class train method instead
1 parent 5457ae1 commit 5e050cf

File tree

6 files changed

+46
-216
lines changed

6 files changed

+46
-216
lines changed

models/LogReg.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,16 @@ def fresh_start(self, params, data_dict):
6161
testy = data_dict['testy']
6262

6363
# supervised logistic regression
64-
clf = linear_model.LogisticRegression(
65-
random_state=self.random_state,
66-
max_iter=params['max_iter'],
67-
tol=params['tol'],
68-
C=params['C']
69-
)
64+
clf = LogReg(params=params, random_state=self.random_state)
7065
# train and test model
71-
clf.fit(trainx, trainy)
72-
clf_pred = clf.predict(testx)
73-
# balanced_accuracy accounts for class imbalanced data
74-
# could alternatively use pure accuracy for a more traditional hyperopt
75-
acc = balanced_accuracy_score(testy, clf_pred)
66+
clf.train(trainx, trainy)
67+
# uses balanced_accuracy accounts for class imbalanced data
68+
clf_pred, acc = clf.predict(testx, testy)
7669

7770
# loss function minimizes misclassification
7871
return {'loss': 1-acc,
7972
'status': STATUS_OK,
80-
'model': clf,
73+
'model': clf.model,
8174
'params': params,
8275
'accuracy': acc}
8376

models/SSML/CoTraining.py

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def __init__(self, params=None, random_state=0):
3535
random_state=self.random_state)
3636
self.model2 = linear_model.LogisticRegression(
3737
random_state=self.random_state)
38+
# default needed for training
39+
self.params = {'n_samples': 1}
3840
else:
3941
self.model1 = linear_model.LogisticRegression(
4042
random_state=self.random_state,
@@ -152,60 +154,17 @@ def fresh_start(self, params, data_dict):
152154
testy = data_dict['testy']
153155
# unlabeled co-training data
154156
Ux = data_dict['Ux']
155-
# avoid overwriting when deleting in co-training loop
156-
U_lr = Ux.copy()
157-
158-
# set the random seed of training splits for reproducibility
159-
# This can be ignored by excluding params['seed']
160-
# in the hyperopt space dictionary
161-
if 'seed' in params.keys():
162-
np.random.seed(params['seed'])
163-
164-
# TODO: allow a user to specify uneven splits between the two models
165-
split_frac = 0.5
166-
# labeled training data
167-
idx = np.random.choice(range(trainy.shape[0]),
168-
size=int(split_frac * trainy.shape[0]),
169-
replace=False)
170157

171-
# avoid overwriting when deleting in co-training loop
172-
L_lr1 = trainx[idx].copy()
173-
L_lr2 = trainx[~idx].copy()
174-
Ly_lr1 = trainy[idx].copy()
175-
Ly_lr2 = trainy[~idx].copy()
158+
clf = CoTraining(params=params, random_state=self.random_state)
159+
# training and testing
160+
model1_accs, model2_accs = clf.train(trainx, trainy, Ux, testx, testy)
161+
# uses balanced_accuracy accounts for class imbalanced data
162+
pred1, acc, pred2, model1_acc, model2_acc = clf.predict(testx, testy)
176163

177-
# initialized logistic regression models for a fresh-start
178-
slr1 = linear_model.LogisticRegression(
179-
random_state=self.random_state,
180-
max_iter=params['max_iter'],
181-
tol=params['tol'],
182-
C=params['C']
183-
)
184-
slr2 = linear_model.LogisticRegression(
185-
random_state=self.random_state,
186-
max_iter=params['max_iter'],
187-
tol=params['tol'],
188-
C=params['C']
189-
)
190-
191-
slr1, slr2, model1_accs, model2_accs = self.training_loop(
192-
slr1, slr2,
193-
L_lr1, L_lr2,
194-
Ly_lr1, Ly_lr2,
195-
U_lr, params['n_samples'],
196-
testx, testy,
197-
)
198-
199-
# balanced_accuracy accounts for class imbalanced data
200-
# could alternatively use pure accuracy for a more traditional hyperopt
201-
model1_acc = balanced_accuracy_score(testy, slr1.predict(testx))
202-
model2_acc = balanced_accuracy_score(testy, slr2.predict(testx))
203-
# select best accuracy for hyperparameter optimization
204-
acc = max(model1_acc, model2_acc)
205164
return {'loss': 1-acc,
206165
'status': STATUS_OK,
207-
'model': slr1,
208-
'model2': slr2,
166+
'model': clf.model1,
167+
'model2': clf.model2,
209168
'model1_acc_history': model1_accs,
210169
'model2_acc_history': model2_accs,
211170
'params': params,
@@ -262,7 +221,7 @@ def optimize(self, space, data_dict, max_evals=50, verbose=True):
262221
self.worst = worst
263222

264223
def train(self, trainx, trainy, Ux,
265-
testx=None, testy=None, n_samples=1, seed=None):
224+
testx=None, testy=None):
266225
'''
267226
Wrapper method for a basic co-training with logistic regression
268227
implementation training method.
@@ -274,9 +233,6 @@ def train(self, trainx, trainy, Ux,
274233
of each model at every iteration.
275234
testy: label vector used for testing the performance
276235
of each model at every iteration.
277-
n_samples: the number of instances to sample and
278-
predict from Ux at one time
279-
seed: set the random seed of training splits for reproducibility
280236
'''
281237

282238
# avoid overwriting when deleting in co-training loop
@@ -285,8 +241,8 @@ def train(self, trainx, trainy, Ux,
285241
# set the random seed of training splits for reproducibility
286242
# This can be ignored by excluding params['seed']
287243
# in the hyperopt space dictionary
288-
if seed is not None:
289-
np.random.seed(seed)
244+
if 'seed' in self.params.keys():
245+
np.random.seed(self.params['seed'])
290246

291247
# TODO: allow a user to specify uneven splits between the two models
292248
split_frac = 0.5
@@ -306,7 +262,7 @@ def train(self, trainx, trainy, Ux,
306262
self.model1, self.model2,
307263
L_lr1, L_lr2,
308264
Ly_lr1, Ly_lr2,
309-
U_lr, n_samples,
265+
U_lr, self.params['n_samples'],
310266
testx, testy,
311267
)
312268

models/SSML/LabelProp.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,32 +72,16 @@ def fresh_start(self, params, data_dict):
7272
testy = data_dict['testy']
7373
Ux = data_dict['Ux']
7474

75-
# combine labeled and unlabeled instances for training
76-
lp_trainx = np.append(trainx, Ux, axis=0)
77-
lp_trainy = np.append(trainy,
78-
np.full(shape=(Ux.shape[0],), fill_value=-1),
79-
axis=0)
80-
81-
# semi-supervised label propagation
82-
clf = semi_supervised.LabelPropagation(
83-
kernel='knn',
84-
gamma=params['gamma'],
85-
n_neighbors=params['n_neighbors'],
86-
max_iter=params['max_iter'],
87-
tol=params['tol'],
88-
n_jobs=-1
89-
)
90-
# train and test model
91-
clf.fit(lp_trainx, lp_trainy)
92-
clf_pred = clf.predict(testx)
93-
# balanced_accuracy accounts for class imbalanced data
94-
# could alternatively use pure accuracy for a more traditional hyperopt
95-
acc = balanced_accuracy_score(testy, clf_pred)
75+
clf = LabelProp(params, random_state=self.random_state)
76+
# training and testing
77+
clf.train(trainx, trainy, Ux)
78+
# uses balanced_accuracy accounts for class imbalanced data
79+
pred, acc = clf.predict(testx, testy)
9680

9781
# loss function minimizes misclassification
9882
return {'loss': 1-acc,
9983
'status': STATUS_OK,
100-
'model': clf,
84+
'model': clf.model,
10185
'params': params,
10286
'accuracy': acc}
10387

models/SSML/ShadowCNN.py

Lines changed: 13 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -207,72 +207,18 @@ def fresh_start(self, params, data_dict):
207207
# unlabeled co-training data
208208
Ux = data_dict['Ux']
209209

210-
# avoid float round-off by using DoubleTensor
211-
xtens = torch.FloatTensor(np.append(trainx,
212-
Ux,
213-
axis=0))[:, ::params['binning']]
214-
# xtens[xtens == 0.0] = torch.unique(xtens)[1]/1e10
215-
ytens = torch.LongTensor(np.append(trainy,
216-
np.full(shape=(Ux.shape[0],),
217-
fill_value=-1),
218-
axis=0))
219-
220-
model = Net(layer1=params['layer1'],
221-
layer2=2*params['layer1'],
222-
layer3=3*params['layer1'],
223-
kernel=params['kernel'],
224-
drop_rate=params['drop_rate'],
225-
length=np.ceil(trainx.shape[1]/params['binning']))
226-
eaat = shadow.eaat.EAAT(model=model,
227-
alpha=params['alpha'],
228-
xi=params['xi'],
229-
eps=params['eps'])
230-
optimizer = optim.SGD(eaat.parameters(),
231-
lr=params['lr'],
232-
momentum=params['momentum'])
233-
234-
# define data set object
235-
dataset = SpectralDataset(xtens, ytens)
236-
237-
# create DataLoader object of DataSet object
238-
DL_DS = torch.utils.data.DataLoader(dataset,
239-
batch_size=params['batch_size'],
240-
shuffle=True)
241-
242-
# labels for unlabeled data are always "-1"
243-
xEnt = torch.nn.CrossEntropyLoss(ignore_index=-1)
244-
245-
n_epochs = 100
246-
eaat.to(self.device)
247-
losscurve = []
248-
evalcurve = []
249-
for epoch in range(n_epochs):
250-
eaat.train()
251-
lossavg = []
252-
for i, (data, targets) in enumerate(DL_DS):
253-
x = data.reshape((data.shape[0],
254-
1,
255-
data.shape[1])).to(self.device)
256-
y = targets.to(self.device)
257-
optimizer.zero_grad()
258-
out = eaat(x)
259-
loss = xEnt(out, y) + eaat.get_technique_cost(x)
260-
loss.backward()
261-
optimizer.step()
262-
lossavg.append(loss.item())
263-
losscurve.append(np.nanmedian(lossavg))
264-
if testx is not None and testy is not None:
265-
pred, acc = self.predict(testx,
266-
testy,
267-
eaat)
268-
evalcurve.append(acc)
269-
270-
if testx is not None and testy is not None:
271-
max_acc = np.max(evalcurve[-25:])
210+
clf = ShadowCNN(params=params,
211+
random_state=self.random_state,
212+
length=trainx.shape[1])
213+
# training and testing
214+
losscurve, evalcurve = clf.train(trainx, trainy, Ux, testx, testy)
215+
# not used; max acc in past few epochs used instead
216+
y_pred, acc = clf.predict(testx, testy)
217+
max_acc = np.max(evalcurve[-25:])
272218

273219
return {'loss': 1-(max_acc/100.0),
274220
'status': STATUS_OK,
275-
'model': eaat,
221+
'model': clf.eaat,
276222
'params': params,
277223
'losscurve': losscurve,
278224
'evalcurve': evalcurve,
@@ -396,15 +342,13 @@ def train(self, trainx, trainy, Ux, testx=None, testy=None):
396342
lossavg.append(loss.item())
397343
losscurve.append(np.nanmedian(lossavg))
398344
if testx is not None and testy is not None:
399-
pred, acc = self.predict(testx,
400-
testy,
401-
self.eaat)
345+
pred, acc = self.predict(testx, testy)
402346
evalcurve.append(acc)
403347

404348
# optionally return the training accuracy if test data was provided
405349
return losscurve, evalcurve
406350

407-
def predict(self, testx, testy=None, eaat=None):
351+
def predict(self, testx, testy=None):
408352
'''
409353
Wrapper method for Shadow NN predict method.
410354
Inputs:
@@ -413,21 +357,15 @@ def predict(self, testx, testy=None, eaat=None):
413357
optional: if included, the predicted classes -and-
414358
the resulting classification accuracy will be returned.
415359
binning: int number of bins sampled in feature vector
416-
model: optional input for testing a given model in hyperparameter
417-
optimization rather than the class saved model.
418360
'''
419361

420-
if eaat is not None:
421-
eval_model = eaat
422-
else:
423-
eval_model = self.eaat
424-
eval_model.eval()
362+
self.eaat.eval()
425363
y_pred, y_true = [], []
426364
for i, data in enumerate(torch.FloatTensor(
427365
testx.copy()[:, ::self.params['binning']])
428366
):
429367
x = data.reshape((1, 1, data.shape[0])).to(self.device)
430-
out = eval_model(x)
368+
out = self.eaat(x)
431369
y_pred.extend(torch.argmax(out, 1).detach().cpu().tolist())
432370
acc = None
433371
if testy is not None:

models/SSML/ShadowNN.py

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -104,59 +104,18 @@ def fresh_start(self, params, data_dict):
104104
# unlabeled co-training data
105105
Ux = data_dict['Ux']
106106

107-
eaat = shadow.eaat.EAAT(model=self.model_factory(
108-
testx[:, ::params['binning']].shape[1],
109-
params['hidden_layer']),
110-
alpha=params['alpha'],
111-
xi=params['xi'],
112-
eps=params['eps']).to(self.device)
113-
eaat_opt = torch.optim.SGD(eaat.parameters(),
114-
lr=params['lr'],
115-
momentum=params['momentum'])
116-
xEnt = torch.nn.CrossEntropyLoss(ignore_index=-1).to(self.device)
117-
118-
# avoid float round-off by using DoubleTensor
119-
xtens = torch.FloatTensor(np.append(trainx,
120-
Ux,
121-
axis=0)[:, ::params['binning']])
122-
# xtens[xtens == 0.0] = torch.unique(xtens)[1]/1e10
123-
ytens = torch.LongTensor(np.append(trainy,
124-
np.full(shape=(Ux.shape[0],),
125-
fill_value=-1),
126-
axis=0))
127-
128-
n_epochs = 100
129-
xt = torch.Tensor(xtens).to(self.device)
130-
yt = torch.LongTensor(ytens).to(self.device)
131-
# saves history for max accuracy
132-
acc_history = []
133-
# set the model into training mode
134-
# NOTE: change this to .eval() mode for testing and back again
135-
eaat.train()
136-
for epoch in range(n_epochs):
137-
# Forward/backward pass for training semi-supervised model
138-
out = eaat(xt)
139-
# supervised + unsupervised loss
140-
loss = xEnt(out, yt) + eaat.get_technique_cost(xt)
141-
eaat_opt.zero_grad()
142-
loss.backward()
143-
eaat_opt.step()
144-
145-
eaat.eval()
146-
eaat_pred = torch.max(eaat(
147-
torch.FloatTensor(
148-
testx.copy()[:, ::params['binning']]
149-
)
150-
), 1)[-1]
151-
acc = shadow.losses.accuracy(eaat_pred,
152-
torch.LongTensor(testy.copy())
153-
).data.item()
154-
acc_history.append(acc)
107+
clf = ShadowNN(params=params,
108+
random_state=self.random_state,
109+
input_length=testx.shape[1])
110+
# training and testing
111+
acc_history = clf.train(trainx, trainy, Ux, testx, testy)
112+
# not used; max acc in past few epochs used instead
113+
eaat_pred, acc = clf.predict(testx, testy)
155114
max_acc = np.max(acc_history[-20:])
156115

157116
return {'loss': 1-(max_acc/100.0),
158117
'status': STATUS_OK,
159-
'model': eaat,
118+
'model': clf.eaat,
160119
'params': params,
161120
'accuracy': (max_acc/100.0)}
162121

tests/test_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def test_CoTraining():
169169

170170
# default behavior
171171
model = CoTraining(params=None, random_state=0)
172-
model.train(X_train, y_train, Ux, seed=0)
172+
model.train(X_train, y_train, Ux)
173173

174174
# testing train and predict methods
175175
pred, acc, *_ = model.predict(X_test, y_test)

0 commit comments

Comments
 (0)