Я начинаю начинать, когда речь идет о внедрении нейронных сетей. Я только что открыл Keras, и я пытаюсь создать очень простую нейронную сеть.
У меня есть куча образов, которые выглядят так, как будто кто-то играет в видеоигр (простая игра, которую я создал в Tkinter):
Идея игры заключается в том, что пользователь управляет ящиком в нижней части экрана, чтобы уклониться от падающих шаров (они могут только уклоняться влево и вправо).
Моя цель состоит в том, чтобы нейронная сеть выводила положение плеера в нижней части экрана. Если они полностью слева, нейронная сеть должна выводить 0
, если они посередине, a .5
, и все в порядке, 1
и все значения между ними.
Мои изображения 300х400 пикселей. Я сохранил свои данные очень просто. Я записал каждое изображение и положение игрока в качестве кортежа для каждого кадра в 50-кадровой игре. Таким образом, моим результатом был список в форме [(image, player position),...]
с 50 элементами. Затем я мариновал этот список.
Поэтому в моем коде я пытаюсь создать чрезвычайно базовую сеть прямой передачи, которая принимает изображение и выводит значение от 0 до 1, представляющее, где находится поле в нижней части изображения. Но моя нейронная сеть выводит только 1 сек.
Что я должен изменить, чтобы заставить его тренировать и выводить ценности, близкие к тем, что я хочу?
Конечно, вот мой код:
# machine learning code mostly from https://machinelearningmastery.com/tutorial-first-neural-network-python-keras/
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
import pickle
def pil_image_to_np_array(image):
'''Takes an image and converts it to a numpy array'''
# from https://stackoverflow.com/a/45208895
# all my images are black and white, so I only need one channel
return np.array(image)[:, :, 0:1]
def data_to_training_set(data):
# split the list in the form [(frame 1 image, frame 1 player position), ...] into [[all images], [all player positions]]
inputs, outputs = [list(val) for val in zip(*data)]
for index, image in enumerate(inputs):
# convert the PIL images into numpy arrays so Keras can process them
inputs[index] = pil_image_to_np_array(image)
return (inputs, outputs)
if __name__ == "__main__":
# fix random seed for reproducibility
np.random.seed(7)
# load data
# data will be in the form [(frame 1 image, frame 1 player position), (frame 2 image, frame 2 player position), ...]
with open("position_data1.pkl", "rb") as pickled_data:
data = pickle.load(pickled_data)
X, Y = data_to_training_set(data)
# get the width of the images
width = X[0].shape[1] # == 400
# convert the player position (a value between 0 and the width of the image) to values between 0 and 1
for index, output in enumerate(Y):
Y[index] = output / width
# flatten the image inputs so they can be passed to a neural network
for index, inpt in enumerate(X):
X[index] = np.ndarray.flatten(inpt)
# keras expects an array (not a list) of image-arrays for input to the neural network
X = np.array(X)
Y = np.array(Y)
# create model
model = Sequential()
# my images are 300 x 400 pixels, so each input will be a flattened array of 120000 gray-scale pixel values
# keep it super simple by not having any deep learning
model.add(Dense(1, input_dim=120000, activation='sigmoid'))
# Compile model
model.compile(loss='mean_squared_error', optimizer='adam')
# Fit the model
model.fit(X, Y, epochs=15, batch_size=10)
# see what the model is doing
predictions = model.predict(X, batch_size=10)
print(predictions) # this prints all 1s! # TODO fix
EDIT: print (Y) дает мне:
так что это определенно не все нули.
Конечно, более глубокая модель может дать вам лучшую точность, но, учитывая тот факт, что ваши изображения просты, довольно простая (мелкая) модель с одним скрытым слоем должна давать среднюю и высокую точность. Итак, вот изменения, которые вам нужны, чтобы это произошло:
Убедитесь, что X
и Y
имеют тип float32
(в настоящее время X
имеет тип uint8
):
X = np.array(X, dtype=np.float32)
Y = np.array(Y, dtype=np.float32)
При обучении нейронной сети было бы намного лучше нормализовать данные обучения. Нормализация помогает процессу оптимизации идти гладко и ускоряет конвергенцию решения. Это также предотвращает большие значения, чтобы вызвать большие обновления градиента, которые были бы разрушительными. Обычно значения каждой функции во входных данных должны падать в небольшом диапазоне, где два общих диапазона: [-1,1]
и [0,1]
. Поэтому, чтобы убедиться, что все значения попадают в диапазон [-1,1]
, мы вычитаем из каждой функции свое среднее значение и делим его на стандартное отклонение:
X_mean = X.mean(axis=0)
X -= X_mean
X_std = X.std(axis=0)
X /= X_std + 1e-8 # add a very small constant to prevent division by zero
Обратите внимание, что мы нормализуем каждую функцию (т.е. каждый пиксель в этом случае) здесь не для каждого изображения. Когда вы хотите прогнозировать новые данные, то есть в режиме вывода или тестирования, вам нужно вычесть X_mean
из тестовых данных и разделить его на X_std
(вы НИКОГДА НИКОГДА не должны вычитать из тестовых данных свое собственное среднее значение или делить его на свое стандартное отклонение; скорее, используйте среднюю и стандартную данные обучения):
X_test -= X_mean
X_test /= X_std + 1e-8
Если вы применяете изменения в точках 1 и 2, вы можете заметить, что сеть больше не предсказывает только одни или только нули. Скорее, он показывает некоторые слабые признаки обучения и предсказывает сочетание нулей и единиц. Это неплохо, но это далеко не хорошо, и у нас большие надежды! Прогнозы должны быть намного лучше, чем сочетание только нулей и единиц. Там вы должны учитывать (забытый!) Курс обучения. Поскольку сеть имеет относительно большое количество параметров с относительно простой проблемой (и есть несколько образцов данных обучения), вы должны выбрать меньшую скорость обучения, чтобы сгладить обновления градиента и процесс обучения:
from keras import optimizers
model.compile(loss='mean_squared_error', optimizer=optimizers.Adam(lr=0.0001))
Вы заметили бы разницу: значение потерь достигает примерно 0.01
после 10 эпох. И сеть больше не предсказывает сочетание нулей и единиц; скорее прогнозы намного точнее и близки к тем, какими они должны быть (т.е. Y
).
Не забывайте! У нас высокие (логические!) Ожидания. Итак, как мы можем сделать лучше, не добавляя новые слои в сеть (очевидно, мы предполагаем, что добавление большего количества слоев может помочь !!)?
4.1. Соберите больше данных о тренировках.
4.2. Добавьте регуляцию веса. Общие из них являются L1 и L2 регуляризация (я настоятельно рекомендую Jupyter ноутбуки на вышла книга Deep Learning с Python, написанной Франсуа Chollet творец Keras. В частности, здесь есть один, который обсуждает упорядочению.)
Вы всегда должны оценивать свою модель надлежащим и непредвзятым образом. Оценивая его на данных обучения (которые вы использовали для обучения), вы ничего не расскажете о том, насколько хорошо ваша модель будет работать на невидимых (то есть новых или реальных) точках данных (например, рассмотрите модель, в которой хранятся или запоминаются все тренировки Это будет отлично работать с данными обучения, но это будет бесполезная модель и плохо работает с новыми данными). Поэтому мы должны иметь тестовые и обучающие наборы данных: мы обучаем модель по данным обучения и оцениваем модель на тестовых (т.е. новых) данных. Однако в процессе разработки хорошей модели вы выполняете множество экспериментов: например, вы сначала меняете тип и количество слоев, обучаете модель, а затем оцениваете ее на тестовых данных, чтобы убедиться, что это хорошо. Затем вы меняете другую вещь, указываете скорость обучения, тренируете ее снова, а затем снова оцениваете ее на тестовых данных... Чтобы сделать это коротко, эти циклы настройки и оценки каким-то образом приводят к переуплотнению тестовых данных. Поэтому нам понадобится третий набор данных, называемый данными валидации (подробнее: в чем разница между набором тестов и набором проверки?):
# first shuffle the data to make sure it isn't in any particular order
indices = np.arange(X.shape[0])
np.random.shuffle(indices)
X = X[indices]
Y = Y[indices]
# you have 200 images
# we select 100 images for training,
# 50 images for validation and 50 images for test data
X_train = X[:100]
X_val = X[100:150]
X_test = X[150:]
Y_train = Y[:100]
Y_val = Y[100:150]
Y_test = Y[150:]
# train and tune the model
# you can attempt train and tune the model multiple times,
# each time with different architecture, hyper-parameters, etc.
model.fit(X_train, Y_train, epochs=15, batch_size=10, validation_data=(X_val, Y_val))
# only and only after completing the tuning of your model
# you should evaluate it on the test data for just one time
model.evaluate(X_test, Y_test)
# after you are satisfied with the model performance
# and want to deploy your model for production use (i.e. real world)
# you can train your model once more on the whole data available
# with the best configurations you have found out in your tunings
model.fit(X, Y, epochs=15, batch_size=10)
(На самом деле, когда у нас мало доступных учебных данных, было бы расточительно отделять валидацию и тестовые данные от целых доступных данных. В этом случае, и если модель не является дорогостоящей вычислительной, вместо разделения набора проверки, который называется перекрестной проверкой, можно выполнить кросс- вализацию K-fold или повторную перекрестную проверку K-fold в случае наличия очень небольшого количества выборок данных.)
На момент написания этого ответа около 4 часов я чувствую сонливость, но я хотел бы упомянуть еще одну вещь, которая напрямую не связана с вашим вопросом: используя библиотеку Numpy и ее функциональные возможности и методы, вы можете написать больше сжатый и эффективный код, а также сэкономить много времени. Поэтому убедитесь, что вы практикуете его более эффективно, так как он широко используется в сообществах и библиотеках машинного обучения. Чтобы продемонстрировать это, вот тот же код, который вы написали, но с большим использованием Numpy (обратите внимание, что я не применял все изменения, упомянутые выше в этом коде):
# machine learning code mostly from https://machinelearningmastery.com/tutorial-first-neural-network-python-keras/
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
import pickle
def pil_image_to_np_array(image):
'''Takes an image and converts it to a numpy array'''
# from https://stackoverflow.com/a/45208895
# all my images are black and white, so I only need one channel
return np.array(image)[:, :, 0]
def data_to_training_set(data):
# split the list in the form [(frame 1 image, frame 1 player position), ...] into [[all images], [all player positions]]
inputs, outputs = zip(*data)
inputs = [pil_image_to_np_array(image) for image in inputs]
inputs = np.array(inputs, dtype=np.float32)
outputs = np.array(outputs, dtype=np.float32)
return (inputs, outputs)
if __name__ == "__main__":
# fix random seed for reproducibility
np.random.seed(7)
# load data
# data will be in the form [(frame 1 image, frame 1 player position), (frame 2 image, frame 2 player position), ...]
with open("position_data1.pkl", "rb") as pickled_data:
data = pickle.load(pickled_data)
X, Y = data_to_training_set(data)
# get the width of the images
width = X.shape[2] # == 400
# convert the player position (a value between 0 and the width of the image) to values between 0 and 1
Y /= width
# flatten the image inputs so they can be passed to a neural network
X = np.reshape(X, (X.shape[0], -1))
# create model
model = Sequential()
# my images are 300 x 400 pixels, so each input will be a flattened array of 120000 gray-scale pixel values
# keep it super simple by not having any deep learning
model.add(Dense(1, input_dim=120000, activation='sigmoid'))
# Compile model
model.compile(loss='mean_squared_error', optimizer='adam')
# Fit the model
model.fit(X, Y, epochs=15, batch_size=10)
# see what the model is doing
predictions = model.predict(X, batch_size=10)
print(predictions) # this prints all 1s! # TODO fix