Keras очень низкая точность, насыщается после нескольких эпох во время обучения.

Вопрос или проблема

Я совсем новичок в области науки о данных и сразу же начал работать с моделями TensorFlow. Ранее я работал с примерами, представленными на сайте. Это моя первая попытка реализовать проект с его использованием.

Я строю предсказатель счёта в крикете, используя Keras и TensorFlow. У меня есть набор данных с деталями игроков в формате csv, который содержит столбцы — “страйкер”, “наносящий удар”, “болт”, “пробежки за мяч”, “средний результат за мяч”, “количество мячей”. “количество мячей” и “пробежки за мяч” являются метками модели, а остальные — признаками. У меня есть всего 51555 строк и 6 столбцов, после разделения 80:20 обучающий набор данных составляет 41244 строки и 6 столбцов.

Вот мой код, здесь много лишнего, но суть можно понять.

import pandas as pd
import numpy as np
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
import seaborn as sns
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Activation, Dense, Dropout, BatchNormalization, Conv2D
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.metrics import categorical_crossentropy
from tensorflow.keras.metrics import KLDivergence
from tensorflow.keras.layers.experimental import preprocessing

df = pd.read_csv('dataset/output_total_run_ball_avg2.csv')
df = df.loc[:,["striker", "bowler", "non_striker", "run_per_ball", "run_per_ball_avg", "ball_count"]]
df = df.sort_values(by=['run_per_ball_avg'])

wordList = []
wordMap = {}
def getNumber(word):
  if word in wordMap:
    return wordMap[word];

  wordIndex = len(wordList)
  wordList.append(word)
  wordMap[word] = wordIndex
  return wordIndex


for name in df["striker"].drop_duplicates():
    df.loc[df['striker'] == name, ['striker']] = getNumber(name)
for name in df["bowler"].drop_duplicates():
    df.loc[df['bowler'] == name, ['bowler']] = getNumber(name)
for name in df["non_striker"].drop_duplicates():
    df.loc[df['non_striker'] == name, ['non_striker']] = getNumber(name)

df['striker'] = df.striker.astype(int)
df['bowler'] = df.bowler.astype(int)
df['non_striker'] = df.non_striker.astype(int)
df.dtypes

sns.pairplot(df[["striker", "bowler", "non_striker", "run_per_ball", "run_per_ball_avg", "ball_count"]], diag_kind='kde')

train_dataset = df.sample(frac=0.8, random_state=0)
test_dataset = df.drop(train_dataset.index)

train_features = train_dataset.loc[:,["striker", "bowler", "non_striker", "run_per_ball_avg"]]
test_features = test_dataset.loc[:,["striker", "bowler", "non_striker", "run_per_ball_avg"]]
train_labels = train_dataset.loc[:,["ball_count", "run_per_ball"]]
test_labels = test_dataset.loc[:,["ball_count", "run_per_ball"]]

normalizer = preprocessing.Normalization()
normalizer.adapt(np.array(train_features))

def build_and_compile_model(norm):
  model = keras.Sequential([
      norm,
      Dense(12, activation='relu'),
      keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer="zeros", gamma_initializer="ones", moving_mean_initializer="zeros", moving_variance_initializer="ones"),
      Dense(64, activation='selu'),
      keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer="zeros", gamma_initializer="ones", moving_mean_initializer="zeros", moving_variance_initializer="ones"),
      Dense(64, activation='elu'),
      keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer="zeros", gamma_initializer="ones", moving_mean_initializer="zeros", moving_variance_initializer="ones"),
      Dense(64, activation='selu'),
      keras.layers.BatchNormalization(axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer="zeros", gamma_initializer="ones", moving_mean_initializer="zeros", moving_variance_initializer="ones"),
      Dense(64, activation='elu'),
      Dense(1)
  ])

  model.compile(loss="mean_squared_error",
                optimizer=SGD(lr=0.00001), metrics=['accuracy'])
  return model

dnn_model = build_and_compile_model(normalizer)
dnn_model.summary()

history = dnn_model.fit(
    train_features, train_labels,
    validation_split=0.2,
    verbose=2, epochs=3000)

Когда я обучаю модель, её производительность низкая и она насыщается всего за несколько эпох. Ниже представлен фрагмент, точность остается такой же как минимум на следующие 1000 эпох.

Epoch 1/3000
1032/1032 - 1s - loss: 15.5479 - accuracy: 0.0984 - val_loss: 13.3904 - val_accuracy: 0.1297
Epoch 2/3000
1032/1032 - 1s - loss: 12.3266 - accuracy: 0.1654 - val_loss: 11.0267 - val_accuracy: 0.2033
Epoch 3/3000
1032/1032 - 1s - loss: 10.3872 - accuracy: 0.2040 - val_loss: 9.4669 - val_accuracy: 0.2104
Epoch 4/3000
1032/1032 - 1s - loss: 9.1706 - accuracy: 0.2088 - val_loss: 8.5238 - val_accuracy: 0.2117
Epoch 5/3000
1032/1032 - 1s - loss: 8.4002 - accuracy: 0.2102 - val_loss: 7.9032 - val_accuracy: 0.2124
Epoch 6/3000
1032/1032 - 1s - loss: 7.9329 - accuracy: 0.2108 - val_loss: 7.5526 - val_accuracy: 0.2127
Epoch 7/3000
1032/1032 - 1s - loss: 7.6496 - accuracy: 0.2110 - val_loss: 7.3502 - val_accuracy: 0.2128
Epoch 8/3000
1032/1032 - 1s - loss: 7.4813 - accuracy: 0.2110 - val_loss: 7.2292 - val_accuracy: 0.2132
Epoch 9/3000
1032/1032 - 1s - loss: 7.3916 - accuracy: 0.2110 - val_loss: 7.1537 - val_accuracy: 0.2135
Epoch 10/3000
1032/1032 - 1s - loss: 7.3251 - accuracy: 0.2111 - val_loss: 7.1124 - val_accuracy: 0.2136
Epoch 11/3000
1032/1032 - 1s - loss: 7.3063 - accuracy: 0.2111 - val_loss: 7.0945 - val_accuracy: 0.2137
Epoch 12/3000
1032/1032 - 1s - loss: 7.2791 - accuracy: 0.2111 - val_loss: 7.0772 - val_accuracy: 0.2139

график данных seaborn
Вот мой график данных из seaborn.

Что я уже попробовал, прочитав различные источники:

  • Попробовал оптимизаторы: Adam, SGD с различными скоростями обучения от 0.001 до 0.000001.
  • Попробовал функции потерь: max_absolute_error, max_squared_error, mse, categorical_crossentropy
  • Нормализовал входные данные
  • Сопоставил имена игроков с индивидуальными номерами
  • Добавил/удалил последовательные слои, 2 – 4 скрытых слоя
  • Использовал пакетную нормализацию

Я много пробовал и много настраивал, но пока без надежды. Я попробовал все возможные методы, предложенные в интернете. Возможно, я делаю что-то глупое. Любая помощь будет очень важна.

Выше приведены мои результаты на данный момент, если потребуется какая-либо дополнительная информация, я постараюсь опубликовать её здесь.
Спасибо всем, кто готов помочь.

На первый взгляд, всё в вашем коде выглядит правильно, кроме lr в коде, который вы поделились (0.00001), кажется низким для SGD, но, как вы упомянули, вы пробовали различные значения, поэтому, вероятно, это не проблема.

Я не знаком с функциями активации elu или selu и обычно использую relu во всех своих слоях, но не могу сказать, является ли это проблемой.

Можете ли вы поделиться ссылкой на набор данных, который вы использовали, чтобы мы могли попробовать запустить код и изменить некоторые параметры?

Несколько вещей, которые сразу бросаются в глаза и кажутся немного странными:

  • У вас кажется есть две метки “ball_count” и “run_per_ball”, но только 1 выход сети.
  • Вы используете MSE потери, которые обычно применяются для регрессионных задач. Это кажется разумным выбором для выходов, таких как ball_count или run_per_ball. Однако, точность является метрикой, которая используется для измерения точности классификационных задач (как количество правильных классификаций, деленное на общее количество образцов).
  • Похоже, вы не используете one hot кодирование для своих категориальных признаков. Если вы этого не сделаете, то сети очень трудно будет извлечь из них полезную информацию.

Что я бы рекомендовал вам делать, так это начать с обучения только на одной из меток, либо ball_count, либо run_per_ball, но не на обеих, и заменить вашу метрику на что-то более подходящее для регрессионной проблемы, например, на среднюю абсолютную ошибку. Когда вы это сделаете, я бы также предложил убрать некоторые из наворотов, такие как пакетная нормализация и сложные активации. Если бы это был я, я бы сделал что-то вроде простой сети с двумя слоями, по 64 единицы в каждом, с активацией ReLU или Tanh и линейной активацией на выходе.

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

Это сложная проблема, если вы новичок в науке о данных. Возможно, стоит сначала поработать с более стандартными проблемами и/или более простыми моделями. Несмотря на то, что нейронные сети сейчас на пике популярности (что справедливо), вам может быть полезно сначала поработать с более простыми линейными моделями. Многие важные основы, которые лежат в основе более простых линейных моделей, также весьма применимы к нейронным сетям. Нейронные сети, как правило, превосходят другие модели, когда есть возможность извлекать более сложные промежуточные признаки из входов, чем сами входы могут предложить, что может не соответствовать данному типу данных.

Ответ или решение

Причины низкой точности модели Keras и ее насыщения на ранних этапах обучения

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

Анализ проблемы

  1. Выбор метрики и функции потерь:

    • Вы используете mean_squared_error как функцию потерь и accuracy как метрику. Это не совсем корректно, поскольку accuracy обычно применяется в задачах классификации, тогда как у вас, судя по переменным ball_count и run_per_ball, задача является регрессионной. Вместо accuracy лучше использовать метрики, такие как mean_absolute_error или mean_squared_error.
  2. Обработка категориальных признаков:

    • Вы используете числовое кодирование для категориальных переменных (например, striker, bowler, non_striker). Это может быть проблемой, так как нейронная сеть может интерпретировать числовые значения как порядковые. Рекомендуется использовать one-hot кодирование, что позволит модели лучше воспринимать категории. Например, можно воспользоваться pd.get_dummies().
  3. Количество выходных данных:

    • У вас есть два метки (ball_count и run_per_ball), но только один выход в модели. Чтобы правильно предсказать обе метки, вам нужно изменить архитектуру сети так, чтобы она имела два выхода (например, через Dense(2) на последнем слое) или выбрать одну из меток для начала.
  4. Активация и слои:

    • Вы используете сложные функции активации (selu, elu). если вы новичок, начните с более простых функций, таких как relu, поскольку они хорошо себя зарекомендовали. Также можно рассмотреть упрощение структуры модели – например, попробовать базовую сеть из одного или двух скрытых слоев с 64 узлами.
  5. Гиперпараметры:

    • Если вы используете алгоритм SGD с низким learning rate (0.00001), это может замедлить обучение. Попробуйте увеличить его (например, до 0.001 или 0.01), чтобы увидеть, изменится ли скорость обучения.
  6. Адаптация к данным и выбор размера выборки:

    • Вы упоминали метод нормализации. Убедитесь, что нормализация выполнена правильно на всех признаках, и что вы не пропускаете какие-либо из них. Также попробуйте увеличить размер обучающей и валидационной выборок, чтобы улучшить обобщающую способность модели.

Рекомендации по улучшению модели

  1. Упростите модель:
    Начните с простой модели с двумя слоями и простыми активациями:

    model = Sequential([
       Dense(64, activation='relu', input_shape=(train_features.shape[1],)),
       Dense(64, activation='relu'),
       Dense(2)  # два выхода
    ])
  2. Используйте подходящую функцию потерь и метрику:
    Замените accuracy на mean_absolute_error:

    model.compile(optimizer='adam', loss='mean_absolute_error')
  3. Обработка данных:
    Убедитесь, что вы используете one-hot кодирование для категориальных переменных:

    df = pd.get_dummies(df, columns=['striker', 'bowler', 'non_striker'])
  4. Увеличьте размер выборки:
    Поэкспериментируйте с различным соотношением для разделения данных (например, 70:30) и убедитесь, что данные не зашумлены.

  5. Визуализация:
    Используйте функции для визуализации потерь и метрик в процессе обучения для лучшего понимания того, как изменения в архитектуре и гиперпараметрах влияют на обучение.

Заключение

Работа с нейронными сетями может быть сложной задачей, особенно когда речь идет о регрессионных задачах и обработке категориальных данных. Начните с простого, упрощения модели и постепенно наращивайте сложность по мере необходимости. Обязательно проверяйте производительность на валидационных данных, чтобы избежать переобучения. Удачи в вашем проекте предсказания результатов в крикете!

Оцените материал
Добавить комментарий

Капча загружается...