Как заставить сеть Keras не выводить все 1с

1

Я начинаю начинать, когда речь идет о внедрении нейронных сетей. Я только что открыл Keras, и я пытаюсь создать очень простую нейронную сеть.

У меня есть куча образов, которые выглядят так, как будто кто-то играет в видеоигр (простая игра, которую я создал в Tkinter):

Изображение 174551

Идея игры заключается в том, что пользователь управляет ящиком в нижней части экрана, чтобы уклониться от падающих шаров (они могут только уклоняться влево и вправо).

Моя цель состоит в том, чтобы нейронная сеть выводила положение плеера в нижней части экрана. Если они полностью слева, нейронная сеть должна выводить 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) дает мне:

Изображение 174551

так что это определенно не все нули.

Теги:
tensorflow
machine-learning
keras
neural-network

1 ответ

1
Лучший ответ

Конечно, более глубокая модель может дать вам лучшую точность, но, учитывая тот факт, что ваши изображения просты, довольно простая (мелкая) модель с одним скрытым слоем должна давать среднюю и высокую точность. Итак, вот изменения, которые вам нужны, чтобы это произошло:

  1. Убедитесь, что X и Y имеют тип float32 (в настоящее время X имеет тип uint8):

    X = np.array(X, dtype=np.float32)
    Y = np.array(Y, dtype=np.float32)
    
  2. При обучении нейронной сети было бы намного лучше нормализовать данные обучения. Нормализация помогает процессу оптимизации идти гладко и ускоряет конвергенцию решения. Это также предотвращает большие значения, чтобы вызвать большие обновления градиента, которые были бы разрушительными. Обычно значения каждой функции во входных данных должны падать в небольшом диапазоне, где два общих диапазона: [-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
    
  3. Если вы применяете изменения в точках 1 и 2, вы можете заметить, что сеть больше не предсказывает только одни или только нули. Скорее, он показывает некоторые слабые признаки обучения и предсказывает сочетание нулей и единиц. Это неплохо, но это далеко не хорошо, и у нас большие надежды! Прогнозы должны быть намного лучше, чем сочетание только нулей и единиц. Там вы должны учитывать (забытый!) Курс обучения. Поскольку сеть имеет относительно большое количество параметров с относительно простой проблемой (и есть несколько образцов данных обучения), вы должны выбрать меньшую скорость обучения, чтобы сгладить обновления градиента и процесс обучения:

    from keras import optimizers
    model.compile(loss='mean_squared_error', optimizer=optimizers.Adam(lr=0.0001))
    

    Вы заметили бы разницу: значение потерь достигает примерно 0.01 после 10 эпох. И сеть больше не предсказывает сочетание нулей и единиц; скорее прогнозы намного точнее и близки к тем, какими они должны быть (т.е. Y).

  4. Не забывайте! У нас высокие (логические!) Ожидания. Итак, как мы можем сделать лучше, не добавляя новые слои в сеть (очевидно, мы предполагаем, что добавление большего количества слоев может помочь !!)?

    4.1. Соберите больше данных о тренировках.

    4.2. Добавьте регуляцию веса. Общие из них являются L1 и L2 регуляризация (я настоятельно рекомендую Jupyter ноутбуки на вышла книга Deep Learning с Python, написанной Франсуа Chollet творец Keras. В частности, здесь есть один, который обсуждает упорядочению.)

  1. Вы всегда должны оценивать свою модель надлежащим и непредвзятым образом. Оценивая его на данных обучения (которые вы использовали для обучения), вы ничего не расскажете о том, насколько хорошо ваша модель будет работать на невидимых (то есть новых или реальных) точках данных (например, рассмотрите модель, в которой хранятся или запоминаются все тренировки Это будет отлично работать с данными обучения, но это будет бесполезная модель и плохо работает с новыми данными). Поэтому мы должны иметь тестовые и обучающие наборы данных: мы обучаем модель по данным обучения и оцениваем модель на тестовых (т.е. новых) данных. Однако в процессе разработки хорошей модели вы выполняете множество экспериментов: например, вы сначала меняете тип и количество слоев, обучаете модель, а затем оцениваете ее на тестовых данных, чтобы убедиться, что это хорошо. Затем вы меняете другую вещь, указываете скорость обучения, тренируете ее снова, а затем снова оцениваете ее на тестовых данных... Чтобы сделать это коротко, эти циклы настройки и оценки каким-то образом приводят к переуплотнению тестовых данных. Поэтому нам понадобится третий набор данных, называемый данными валидации (подробнее: в чем разница между набором тестов и набором проверки?):

    # 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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню