-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeras_mlp.py
185 lines (160 loc) · 9.19 KB
/
keras_mlp.py
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
""" Это – класс для обучения нейросети на библиотеке Keras. """
from keras import optimizers, regularizers
from keras.models import Sequential, load_model
from keras.layers import Dense, Activation, Dropout
from keras.callbacks import EarlyStopping
from helpers.get_optimizer import get_optimizer
import os.path
# импортируем список всех возможных опций
from helpers.choose_parameters import options
# функция, которая подбирает количество и параметры слоёв дропаута в зависимости
# от того, что передано при инициализации класса
from helpers.define_dropout import define_dropout
# внешняя функция, которая подбирает количество функций активации
from helpers.define_activations import define_activations
class Keras_MLP():
def __init__(self,
task, # название задачи
layer_sizes, # котреж скрытых слоёв
activations,
dropout, # указываем, нужен ли дропаут и если да, то сколько -
# дропаут слой идёт после каждого скрытого слоя
alpha,
batch_size,
learning_rate_init,
epochs,
shuffle,
verbose,
early_stopping,
optimizer_name,
**kwargs):
self.task = task # ввод названия задачи
self.layer_sizes = layer_sizes
self.activations = activations
# activations может быть "Auto", "relu", ["relu", "relu"]
self.dropout = dropout
# dropout может быть строкой ("Auto"), массивом с 1 числом или несколькими
self.alpha = alpha
self.batch_size = batch_size
self.learning_rate_init = learning_rate_init
self.epochs = epochs
self.shuffle = shuffle
self.verbose = verbose
self.early_stopping = early_stopping
self.optimizer_name = optimizer_name
for key, value in kwargs.items():
setattr(self, key, value)
def fit(self, x_train, y_train):
"""
Принимает данные.
Создаёт модель на основании параметров, переданных в __init__.
Обучает модель.
Возвращает обученную модель.
"""
try:
# массив. 1 – функция активации для последнего слоя, 2 - функция ошибки
chosen_task = options[self.task]
except Exception:
print("No such task found in choose_parameters.py. Reverting to default")
chosen_task = options["default"]
# Создаём пустую модель (шампур, на который потом будем нанизывать слои)
model = Sequential()
# Сохраняем shape переданных данных
# x_train_shape нужен для самого первого слоя (размерность вход. данных)
# y_train_shape нужен для последнего слоя (размерность выход. данных)
x_train_shape = int(x_train.shape[1])
y_train_shape = int(y_train.shape[1])
# возращает массив с параметрами для дропаута (может быть None, though!)
used_dropout = define_dropout(self.dropout, self.layer_sizes)
used_activations = define_activations(self.activations, self.layer_sizes)
indices = [i for i in range(0, len(self.layer_sizes))]
for index, layer_size, dropout, activation in zip(indices, self.layer_sizes, used_dropout, used_activations):
if index == 0:
model.add(Dense(layer_size,
input_dim=x_train_shape,
kernel_regularizer=regularizers.l2(self.alpha)))
model.add(Activation(activation))
model.add(Dropout(dropout))
else:
model.add(Dense(layer_size,
kernel_regularizer=regularizers.l2(self.alpha)))
model.add(Activation(activation))
model.add(Dropout(dropout))
# отдельно после всего добавляем последний слой. Размер последнего слоя
# равен размерности y_train. К нему же добавляем активатор по выбранной
# задаче
model.add(Dense(y_train_shape,
kernel_regularizer=regularizers.l2(self.alpha)))
model.add(Activation(chosen_task[0]))
chosen_optimizer = None
# Возможны два пути:
#
# 1 путь. Собираем в один словарь все переменные класса
# и передаём их в get_optimizer(), где kwargs.get()
# всё равно среди этой кучи найдет нужные ей значения.
#
# 2 путь. Из всех переменных класса выделяем только последние
# параметры (**kwargs), которые затем передаём в
# get_optimizer(), где kwargs.get() тоже будет искать
# нужные ей параметры, но в гораздо меньшей куче
#
# Была проведена проверка скорости выполнения обоих вариантов
# с помощью стандартной функции timeit() на 10000 повторах.
# См. файл kwargs_get_speedtest.py для подробностей.
#
# Результаты:
# - Словарь со всеми переменными класса: 155.58 сек
# - Словарь только с нужными переменными: 155.70 сек
#
# Вывод: оба варианта эквивалентны, поэтому оставляю первый
# вариант как самый удобочитаемый и экономящий место (1 строка
# вместо 5)
chosen_optimizer = get_optimizer(self.optimizer_name, self.__dict__)
# Проверяем, указаны ли в **kwargs параметры loss_function и metrics,
# чтобы не привязываться к файлу choose_parameters.py
parameters_to_compile = []
if self.loss_function:
parameters_to_compile.append(self.loss_function)
else:
parameters_to_compile.append(chosen_task[1])
if self.metrics:
parameters_to_compile.append(self.metrics)
else:
parameters_to_compile.append(chosen_task[2])
model.compile(loss=parameters_to_compile[0], # функция ошибки под задачу - - -
optimizer=chosen_optimizer,
metrics=parameters_to_compile[1]) # метрика под задачу - - - - -
print("Model Summary:\n\n")
model.summary()
# Так называемые колл-бэки Keras принимает только в виде массива
used_callbacks = []
# На тот случай, если вдруг понадобится поддержка EarlyStopping,
# код честно нашел где-то на гитхабе в обсуждениях.
if self.early_stopping == True:
early_stopping_callback = EarlyStopping(monitor="value_loss")
used_callbacks.append(early_stopping_callback)
# Нужно для большей совместимости с прежним кодом
if self.batch_size == "auto":
self.batch_size = 200
model.fit(x_train,
y_train,
batch_size = self.batch_size,
epochs = self.epochs,
verbose = self.verbose,
callbacks = used_callbacks,
shuffle = self.shuffle)
return model
def partial_fit(self, x_train, y_train):
# метод для обучения сети в несколько заходов
# если файл с обученной моделью уже существует,
keras_model_filename="trained_keras_model.h5" # не надо хардкодить!
if os.path.isfile(keras_model_filename):
# загрузить его
model = load_model(keras_model_filename)
# дообучить модель
model.fit(x_train, y_train)
# сохранить модель
model.save(keras_model_filename)
else:
model = self.fit(x_train, y_train)
model.save(keras_model_filename)