|
| 1 | +""" |
| 2 | + Simulate multi-label classification. |
| 3 | +""" |
1 | 4 | import numpy as np
|
2 | 5 | import keras.backend as K
|
3 | 6 | from keras.models import Model
|
4 | 7 | from keras.layers import Dense, Activation, Input
|
| 8 | +import keras.regularizers as regs |
| 9 | +import keras.constraints as constraints |
5 | 10 | import matplotlib.pyplot as plt
|
| 11 | +from sklearn.datasets import make_multilabel_classification |
| 12 | +from sklearn.model_selection import train_test_split |
| 13 | +from sklearn import preprocessing |
6 | 14 | import src.model.mfom as mfom
|
7 | 15 | import src.utils.metrics as MT
|
8 | 16 | import src.model.objectives as obj
|
9 | 17 |
|
10 |
| -np.random.seed(777) |
| 18 | +RANDOM_SEED = 777 |
| 19 | +np.random.seed(RANDOM_SEED) |
11 | 20 |
|
12 | 21 |
|
13 |
| -def generate_dataset(output_dim=14, num_examples=10000): |
14 |
| - """ |
15 |
| - Summation of two binary numbers. |
16 |
| - Input is two binary numbers, stacked in one vector. |
17 |
| - Output is an integer number. |
18 |
| - """ |
| 22 | +def generate_dataset(n_smp=300, ratio=0.3, n_feat=2, n_cls=2): |
| 23 | + x, y = make_multilabel_classification(n_samples=n_smp, n_features=n_feat, |
| 24 | + n_classes=n_cls, n_labels=1, |
| 25 | + allow_unlabeled=False, |
| 26 | + random_state=RANDOM_SEED) |
| 27 | + scaler = preprocessing.StandardScaler() |
| 28 | + x = scaler.fit_transform(x) |
| 29 | + x_tr, x_tst, y_tr, y_tst = train_test_split(x, y, test_size=ratio, random_state=RANDOM_SEED) |
| 30 | + return x_tr, x_tst, y_tr, y_tst |
19 | 31 |
|
20 |
| - def int2vec(x, dim=output_dim): |
21 |
| - out = np.zeros(dim) |
22 |
| - binrep = np.array(list(np.binary_repr(x))).astype('int') |
23 |
| - out[-len(binrep):] = binrep |
24 |
| - return out |
25 | 32 |
|
26 |
| - x_left_int = (np.random.rand(num_examples) * 2 ** (output_dim - 1)).astype('int') |
27 |
| - x_right_int = (np.random.rand(num_examples) * 2 ** (output_dim - 1)).astype('int') |
28 |
| - y_int = x_left_int + x_right_int |
29 |
| - |
30 |
| - x = list() |
31 |
| - for i in range(len(x_left_int)): |
32 |
| - x.append(np.concatenate((int2vec(x_left_int[i]), int2vec(x_right_int[i])))) |
33 |
| - |
34 |
| - y = list() |
35 |
| - for i in range(len(y_int)): |
36 |
| - y.append(int2vec(y_int[i])) |
37 |
| - |
38 |
| - x = np.array(x) |
39 |
| - y = np.array(y) |
40 |
| - return x, y |
41 |
| - |
42 |
| - |
43 |
| -if __name__ == '__main__': |
44 |
| - dim = 14 |
45 |
| - nclass = 7 |
46 |
| - |
47 |
| - # Input block |
48 |
| - feat_input = Input(shape=(dim,), name='main_input') |
| 33 | +def mfom_model(in_dim, nclass): |
| 34 | + # input block |
| 35 | + feat_input = Input(shape=(in_dim,), name='main_input') |
49 | 36 | # layer 1
|
50 |
| - x = Dense(30, name='dense1')(feat_input) |
| 37 | + x = Dense(10, name='dense1')(feat_input) |
51 | 38 | x = Activation(activation='sigmoid', name='act1')(x)
|
| 39 | + # layer 2 |
| 40 | + x = Dense(10, name='dense2')(x) |
| 41 | + x = Activation(activation='sigmoid', name='act2')(x) |
52 | 42 | # output layer
|
53 | 43 | x = Dense(nclass, name='pre_activation')(x)
|
54 | 44 | y_pred = Activation(activation='sigmoid', name='output')(x)
|
55 | 45 |
|
| 46 | + # === MFoM head === |
56 | 47 | # misclassification layer, feed Y
|
57 | 48 | y_true = Input(shape=(nclass,), name='y_true')
|
58 | 49 | psi = mfom.UvZMisclassification(name='uvz_misclass')([y_true, y_pred])
|
59 | 50 |
|
60 | 51 | # class Loss function layer
|
61 |
| - out = mfom.SmoothErrorCounter(name='smooth_error_counter')(psi) |
| 52 | + # NOTE: you may want to add regularization or constraints |
| 53 | + out = mfom.SmoothErrorCounter(name='smooth_error_counter', |
| 54 | + # alpha_constraint=constraints.min_max_norm(min_value=-4., max_value=4.), |
| 55 | + # alpha_regularizer=regs.l1(0.001), |
| 56 | + # beta_constraint=constraints.min_max_norm(min_value=-4., max_value=4.), |
| 57 | + # beta_regularizer=regs.l1(0.001) |
| 58 | + )(psi) |
62 | 59 |
|
63 | 60 | # compile model
|
64 | 61 | model = Model(input=[y_true, feat_input], output=out)
|
65 |
| - model.compile(loss=obj.mfom_eer_normalized, optimizer='Adam') |
66 |
| - model.summary() |
| 62 | + return model |
67 | 63 |
|
68 |
| - # train |
69 |
| - X, Y = generate_dataset(output_dim=nclass) |
70 |
| - hist = model.fit([Y, X], Y, nb_epoch=100, batch_size=16) |
71 | 64 |
|
72 |
| - # calc accuracy: we cut MFoM head, up to sigmoid output |
| 65 | +def cut_mfom(model): |
| 66 | + # calc accuracy: cut MFoM head, up to sigmoid output |
73 | 67 | input = model.get_layer(name='main_input').output
|
74 | 68 | out = model.get_layer(name='output').output
|
75 |
| - cut_model = Model(input=input, output=out) |
76 |
| - y_pred = cut_model.predict(X) |
77 |
| - eer_val = MT.eer(y_true=Y.flatten(), y_pred=y_pred.flatten()) |
| 69 | + cut_net = Model(input=input, output=out) |
| 70 | + return cut_net |
| 71 | + |
| 72 | + |
| 73 | +if __name__ == '__main__': |
| 74 | + dim = 20 |
| 75 | + nclass = 10 |
| 76 | + |
| 77 | + # mfom model |
| 78 | + model = mfom_model(dim, nclass) |
| 79 | + model.compile(loss=obj.mfom_eer_normalized, optimizer='Adam') |
| 80 | + model.summary() |
| 81 | + |
| 82 | + # training on multi-label dataset |
| 83 | + x_train, x_test, y_train, y_test = generate_dataset(n_smp=10000, n_feat=dim, n_cls=nclass) |
| 84 | + mask = y_train.sum(axis=-1) != nclass |
| 85 | + y_train = y_train[mask] |
| 86 | + x_train = x_train[mask] |
| 87 | + hist = model.fit([y_train, x_train], y_train, nb_epoch=10, batch_size=16) |
| 88 | + |
| 89 | + # cut MFoM head |
| 90 | + cut_model = cut_mfom(model) |
| 91 | + y_pred = cut_model.predict(x_test) |
| 92 | + |
| 93 | + # evaluate |
| 94 | + eer_val = MT.eer(y_true=y_test.flatten(), y_pred=y_pred.flatten()) |
78 | 95 | print('EER: %.4f' % eer_val)
|
79 | 96 |
|
80 |
| - # history plot, alpha and beta params |
| 97 | + # history plot, alpha and beta params of MFoM |
81 | 98 | m = model.get_layer('smooth_error_counter')
|
82 | 99 | print('alpha: ', K.get_value(m.alpha))
|
83 | 100 | print('beta: ', K.get_value(m.beta))
|
|
0 commit comments