-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDeep_RVFL.py
More file actions
268 lines (227 loc) · 10.3 KB
/
Copy pathDeep_RVFL.py
File metadata and controls
268 lines (227 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#-- coding: utf-8 --
#@Time : 2021/3/27 20:40
#@Author : HUANG XUYANG
#@Email : xhuang032@e.ntu.edu.sg
#@File : Deep_RVFL.py
#@Software: PyCharm
import numpy as np
class DeepRVFL:
"""A deep RVFL classifier or regression.
Attributes:
n_nodes: An integer of enhancement node number.
lam: A floating number of regularization parameter.
w_random_vec_range: A list, [min, max], the range of generating random weights.
b_random_vec_range: A list, [min, max], the range of generating random bias.
random_weights: A Numpy array shape is [n_feature, n_nodes], weights of neuron.
random_bias: A Numpy array shape is [n_nodes], bias of neuron.
beta: A Numpy array shape is [n_feature + n_nodes, n_class], the projection matrix.
activation: A string of activation name.
n_layer: A integer, N=number of hidden layers.
data_std: A list, store normalization parameters for each layer.
data_mean: A list, store normalization parameters for each layer.
same_feature: A bool, the true means all the features have same meaning and boundary for example: images.
task_type: A string of ML task type, 'classification' or 'regression'.
"""
def __init__(self, n_nodes, lam, w_random_vec_range, b_random_vec_range, activation, n_layer, same_feature=False,
task_type='classification'):
assert task_type in ['classification', 'regression'], 'task_type should be "classification" or "regression".'
self.n_nodes = n_nodes
self.lam = lam
self.w_random_range = w_random_vec_range
self.b_random_range = b_random_vec_range
self.random_weights = []
self.random_bias = []
self.beta = None
a = Activation()
self.activation_function = getattr(a, activation)
self.n_layer = n_layer
self.data_std = [None] * self.n_layer
self.data_mean = [None] * self.n_layer
self.same_feature = same_feature
self.task_type = task_type
def train(self, data, label, n_class):
"""
:param data: Training data.
:param label: Training label.
:param n_class: An integer of number of class. In regression, this parameter won't be used.
:return: No return
"""
assert len(data.shape) > 1, 'Data shape should be [n, dim].'
assert len(data) == len(label), 'Label number does not match data number.'
assert len(label.shape) == 1, 'Label should be 1-D array.'
n_sample = len(data)
n_feature = len(data[0])
d = self.standardize(data, 0) # Normalization data
h = data.copy()
for i in range(self.n_layer):
h = self.standardize(h, i) # Normalization data
self.random_weights.append(self.get_random_vectors(len(h[0]), self.n_nodes, self.w_random_range))
self.random_bias.append(self.get_random_vectors(1, self.n_nodes, self.b_random_range))
h = self.activation_function(np.dot(h, self.random_weights[i]) + np.dot(np.ones([n_sample, 1]),
self.random_bias[i]))
d = np.concatenate([h, d], axis=1)
d = np.concatenate([d, np.ones_like(d[:, 0:1])], axis=1)
if self.task_type == 'classification':
y = self.one_hot(label, n_class)
else:
y = label
if n_sample > (self.n_nodes * self.n_layer + n_feature):
self.beta = np.linalg.inv((self.lam * np.identity(d.shape[1]) + np.dot(d.T, d))).dot(d.T).dot(y)
else:
self.beta = d.T.dot(np.linalg.inv(self.lam * np.identity(n_sample) + np.dot(d, d.T))).dot(y)
def predict(self, data):
"""
:param data: Predict data.
:return: When classification, return Prediction result and probability.
When regression, return the output of rvfl.
"""
n_sample = len(data)
d = self.standardize(data, 0)
h = data.copy()
for i in range(self.n_layer):
h = self.standardize(h, i)
h = self.activation_function(np.dot(h, self.random_weights[i]) + np.dot(np.ones([n_sample, 1]),
self.random_bias[i]))
d = np.concatenate([h, d], axis=1)
d = np.concatenate([d, np.ones_like(d[:, 0:1])], axis=1)
output = np.dot(d, self.beta)
if self.task_type == 'classification':
proba = self.softmax(output)
result = np.argmax(proba, axis=1)
return result, proba
elif self.task_type == 'regression':
return output
def eval(self, data, label):
"""
:param data: Evaluation data.
:param label: Evaluation label.
:return: When classification return accuracy.
When regression return MAE.
"""
assert len(data.shape) > 1, 'Data shape should be [n, dim].'
assert len(data) == len(label), 'Label number does not match data number.'
assert len(label.shape) == 1, 'Label should be 1-D array.'
n_sample = len(data)
d = self.standardize(data, 0)
h = data.copy()
for i in range(self.n_layer):
h = self.standardize(h, i)
h = self.activation_function(np.dot(h, self.random_weights[i]) + np.dot(np.ones([n_sample, 1]),
self.random_bias[i]))
d = np.concatenate([h, d], axis=1)
d = np.concatenate([d, np.ones_like(d[:, 0:1])], axis=1)
output = np.dot(d, self.beta)
if self.task_type == 'classification':
result = np.argmax(output, axis=1)
acc = np.sum(np.equal(result, label)) / len(label)
return acc
elif self.task_type == 'regression':
mae = np.mean(np.abs(output - label))
return mae
@staticmethod
def get_random_vectors(m, n, scale_range):
x = (scale_range[1] - scale_range[0]) * np.random.random([m, n]) + scale_range[0]
return x
@staticmethod
def one_hot(x, n_class):
y = np.zeros([len(x), n_class])
for i in range(len(x)):
y[i, x[i]] = 1
return y
def standardize(self, x):
if self.same_feature is True:
if self.data_std is None:
self.data_std = np.maximum(np.std(x), 1/np.sqrt(len(x)))
if self.data_mean is None:
self.data_mean = np.mean(x)
return (x - self.data_mean) / self.data_std
else:
if self.data_std is None:
self.data_std = np.maximum(np.std(x, axis=0), 1/np.sqrt(len(x)))
if self.data_mean is None:
self.data_mean = np.mean(x, axis=0)
return (x - self.data_mean) / self.data_std
@staticmethod
def softmax(x):
return np.exp(x) / np.repeat((np.sum(np.exp(x), axis=1))[:, np.newaxis], len(x[0]), axis=1)
class Activation:
@staticmethod
def sigmoid(x):
return 1 / (1 + np.e ** (-x))
@staticmethod
def sine(x):
return np.sin(x)
@staticmethod
def hardlim(x):
return (np.sign(x) + 1) / 2
@staticmethod
def tribas(x):
return np.maximum(1 - np.abs(x), 0)
@staticmethod
def radbas(x):
return np.exp(-(x**2))
@staticmethod
def sign(x):
return np.sign(x)
@staticmethod
def relu(x):
return np.maximum(0, x)
if __name__ == '__main__':
import sklearn.datasets as sk_dataset
def prepare_data_classify(proportion):
dataset = sk_dataset.load_breast_cancer()
label = dataset['target']
data = dataset['data']
n_class = len(dataset['target_names'])
shuffle_index = np.arange(len(label))
np.random.shuffle(shuffle_index)
train_number = int(proportion * len(label))
train_index = shuffle_index[:train_number]
val_index = shuffle_index[train_number:]
data_train = data[train_index]
label_train = label[train_index]
data_val = data[val_index]
label_val = label[val_index]
return (data_train, label_train), (data_val, label_val), n_class
def prepare_data_regression(proportion):
dataset = sk_dataset.load_diabetes()
label = dataset['target']
data = dataset['data']
shuffle_index = np.arange(len(label))
np.random.shuffle(shuffle_index)
train_number = int(proportion * len(label))
train_index = shuffle_index[:train_number]
val_index = shuffle_index[train_number:]
data_train = data[train_index]
label_train = label[train_index]
data_val = data[val_index]
label_val = label[val_index]
return (data_train, label_train), (data_val, label_val)
num_nodes = 2 # Number of enhancement nodes.
regular_para = 1 # Regularization parameter.
weight_random_range = [-1, 1] # Range of random weights.
bias_random_range = [0, 1] # Range of random weights.
num_layer = 2 # Number of hidden layers
# Classification
train, val, num_class = prepare_data_classify(0.8)
deep_rvfl = DeepRVFL(n_nodes=num_nodes, lam=regular_para, w_random_vec_range=weight_random_range,
b_random_vec_range=bias_random_range, activation='relu', n_layer=num_layer, same_feature=False,
task_type='classification')
deep_rvfl.train(train[0], train[1], num_class)
prediction, proba = deep_rvfl.predict(val[0])
accuracy = deep_rvfl.eval(val[0], val[1])
print('Acc:', accuracy)
# Regression
num_nodes = 10 # Number of enhancement nodes.
regular_para = 1 # Regularization parameter.
weight_random_range = [-1, 1] # Range of random weights.
bias_random_range = [0, 1] # Range of random weights.
num_layer = 2 # Number of hidden layers
train, val = prepare_data_regression(0.8)
deep_rvfl = DeepRVFL(n_nodes=num_nodes, lam=regular_para, w_random_vec_range=weight_random_range,
b_random_vec_range=bias_random_range, activation='relu', n_layer=num_layer, same_feature=False,
task_type='regression')
deep_rvfl.train(train[0], train[1], 0)
prediction = deep_rvfl.predict(val[0])
mae = deep_rvfl.eval(val[0], val[1])
print('MAE:', mae)