Как я могу запустить условную нейронную сеть в Python (аналогично Clogit в R / Stata)? - PullRequest
3 голосов
/ 20 июня 2019

Я собираюсь обучить нейронную сеть в Керасе (или тензорный поток, если требуется), которая предсказывает, какие три игрока обозначены звездами в каждой хоккейной игре НХЛ. Моя матрица Х состоит из стандартной информации о счете для каждого игрока, например, количество сыгранных минут, количество голов, передач и т. д. и переменная цели могут быть одного из четырех классов (0 - не в трех лучших, 1 - лучший игрок, 2 - лучший игрок, 3 - третий лучший игрок).

Пока что это очень стандартная проблема, и ванильная нейронная сеть делает разумную работу по прогнозированию вероятности того, что игрок будет назначен звездой. Проблема заключается в том, что я хочу добавить ограничение, которое заставляет вероятности в пределах одной игры суммироваться с одним (по всем игрокам) для классов 1, 2, 3, в то же время сохраняя ограничение softmax, согласно которому для каждого игрока вероятность всех классов равна каждому. должен также подвести к одному. Я отмечаю, что модель условной логистической регрессии достигает этого для двоичной классификации, но я не видел, чтобы это реализовывалось в рамках машинного обучения.

Полагаю, для этого потребуется специальная функция активации, которую Keras может реализовать. Однако я не уверен, как будет выглядеть математика / код.

Пример кода, который я до сих пор выполнял, таков:

# import packages
import numpy as np
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import Adam

# generate data
games, m = 50, 40
X = np.zeros((games * m, 11))
Y = np.zeros((games * m, 4))

for i in range(games):
    rowStart, rowEnd = (i) * m, (1 + i) * m
    X[rowStart:rowEnd, 0] = i
    X[rowStart:rowEnd, 1:] = np.random.rand(m, 10)  

    for j in range(1, 4):
        rowInd = rowStart + np.random.randint(0, m)
        while np.sum(Y[rowInd]) != 0.0:
            rowInd = rowStart + np.random.randint(0, m)
        Y[rowInd, j] = 1    

# run model   
mod2 = Sequential()
mod2.add(Dense(10, activation='relu', input_shape=(X.shape[1]-1,)))
mod2.add(Dense(6, activation='relu'))
mod2.add(Dense(4, activation='softmax'))
adam = Adam(lr=0.001)
mod2.compile(optimizer=adam, loss='categorical_crossentropy', metrics=['categorical_accuracy'])
hist = mod2.fit(X[:,1:], Y, epochs=10, batch_size=m)

1 Ответ

0 голосов
/ 21 июня 2019

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

Но есть и другие способырешить это без «двунаправленного софтмакса» и без нарушения ваших ограничений.Я предлагаю вам использовать полную игру для 40 игроков с 10 функциями в качестве входных данных и ранжировать каждого из 40 игроков в качестве выходных данных.Вместо того, чтобы классифицировать каждого игрока на 4 класса, я предлагаю вам дать каждому игроку очки (например, -1 для не лучших трех, 0 для третьего лучшего игрока, 1 для второго лучшего игрока и 2 для лучшего игрока).По прогнозу, вы можете просто выбрать игрока с наибольшим счетом в качестве лучшего игрока, второго по величине в качестве второго лучшего, третьего по величине в качестве третьего лучшего, а остальные не в лучших трех.Таким образом, вы не нарушаете свое ограничение, согласно которому каждый игрок должен получить «класс» и только одного игрока за первое, второе и третье место.

см. Ниже минимальный рабочий пример:

# import packages
import numpy as np
from tensorflow.keras.layers import Dense, Input, Flatten#, Reshape
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam

# generate data
games, m = 50, 40
X = np.zeros((games, m, 11))
Y = np.zeros((games, m))

for i in range(games):
    X[i, :, 0] = i
    X[i, :, 1:] = np.random.rand(m, 10)  

    y_indexes = np.arange(m)
    np.random.shuffle(y_indexes)
    # score players
    Y[i,y_indexes[0]] = 2 # best
    Y[i,y_indexes[1]] = 1 # second best
    Y[i,y_indexes[2]] = 0 # third best
    Y[i,y_indexes[3:]] = -1 # not best

# run model   
inputs = Input(shape=(m,10)) # -1 as we dont use fist column (game number)
inputs_flatten = Flatten()(inputs)
x = Dense(1024, activation='relu')(inputs_flatten)
x = Dense(512, activation='relu')(x)
x = Dense(256, activation='relu')(x)
outputs = Dense(m, activation=None)(x)
model = Model(inputs = inputs, outputs = outputs)

adam = Adam(lr=0.001)
model.compile(optimizer=adam, loss='mse', metrics=['accuracy'])
hist = model.fit(X[:,:,1:], Y, epochs=20, batch_size=10)

# predict third, second and best players for the first game
# the print number, is the player number
Y_pred = model.predict(X[0:1,:,1:])
print(np.argsort(Y_pred.reshape(-1))[-3:])
#[7 29 19]
# True best players fist game
print(np.argsort(Y[0,:].reshape(-1))[-3:])
#[7 29 19]

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

...