Прогнозирование по нескольким меткам с использованием DNN

Я пытаюсь предсказать несколько меток для данного текста. Это хорошо работает для одной метки, но я не знаю, как реализовать показатель достоверности для предсказания нескольких меток.

У меня есть данные в следующем денормализованном формате:

┌────┬──────────┬────────┐
│ id │  Topic   │  Text  │
├────┼──────────┼────────┤
│  1 │ Apples   │ FooBar │
│  1 │ Oranges  │ FooBar │
│  1 │ Kiwis    │ FooBar │
│  2 │ Potatoes │ BazBak │
│  3 │ Carrot   │ BalBan │
└────┴──────────┴────────┘

Каждому тексту может быть назначена одна или несколько тем. Пока я придумал это. Сначала я подготавливаю свои данные — токенизация, стемм и т. д.

df = #read data from csv
categories = [ "Apples", "Oranges", "Kiwis", "Potatoes", "Carrot"]
words = []
docs = []

for index, row in df.iterrows():
    stems = tokenize_and_stem(row, stemmer)
    words.extend(stems)
    docs.append((stems, row[1]))

# remove duplicates
words = sorted(list(set(words)))

# create training data
training = []
output = []
# create an empty array for our output
output_empty = [0] * len(categories)


for doc in docs:
    # initialize our bag of words(bow) for each document in the list
    bow = []
    # list of tokenized words for the pattern
    token_words = doc[0]

    # create our bag of words array
    for w in words:
        bow.append(1) if w in token_words else bow.append(0)

    output_row = list(output_empty)
    output_row[categories.index(doc[1])] = 1

    # our training set will contain a the bag of words model and the output row that tells which catefory that bow belongs to.
    training.append([bow, output_row])

# shuffle our features and turn into np.array as tensorflow  takes in numpy array
random.shuffle(training)
training = np.array(training)

# trainX contains the Bag of words and train_y contains the label/ category
train_x = list(training[:, 0])
train_y = list(training[:, 1])

Далее я создаю свою модель обучения

# reset underlying graph data
tf.reset_default_graph()
# Build neural network
net = tflearn.input_data(shape=[None, len(train_x[0])])
net = tflearn.fully_connected(net, 8)
net = tflearn.fully_connected(net, 8)
net = tflearn.fully_connected(net, len(train_y[0]), activation='softmax')
net = tflearn.regression(net)

# Define model and setup tensorboard
model = tflearn.DNN(net, tensorboard_dir='tflearn_logs')
# Start training (apply gradient descent algorithm)
model.fit(train_x, train_y, n_epoch=1000, batch_size=8, show_metric=True)
model.save('model.tflearn')

После этого я пытаюсь предсказать свои темы:

df = # read data from excel

for index, row in df.iterrows():
    prediction = model.predict([get_bag_of_words(row[2])])
    return categories[np.argmax(prediction)]

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

Какие-либо предложения?


person Kertis van Kertis    schedule 22.01.2018    source источник


Ответы (1)


Вместо использования активации softmax на выходном слое вы должны использовать активацию sigmoid. Ваша функция потерь должна быть кросс-энтропийной. Это ключевое изменение, которое вам нужно для мультикласса.

Проблема с softmax заключается в том, что он создает распределение вероятностей по вашим результатам. Таким образом, если классы A и B сильно представлены, softmax над 3 классами может дать вам результат, например [0,49, 0,49, 0,02], но вы бы предпочли что-то более похожее на [0,99, 0,99, 0,01].

Сигмовидная активация делает именно это, она сжимает действительные логиты (значение последнего слоя перед применением преобразования) в диапазон [0, 1] (что необходимо для использования функции кросс-энтропийных потерь). И он делает это для каждого выхода независимо.

person David Parks    schedule 22.01.2018
comment
Я добавил activation='sigmoid' ко всем слоям и определил свою функцию потерь как loss='categorical_crossentropy' в tflearn.regression. Я все еще получаю ненормализованные значения, такие как [[8.9157884e-06 9.7833133e-01 8.3094416e-03 3.3070598e-02 4.0033931e-01]] - person Kertis van Kertis; 22.01.2018
comment
Сигмоид необходим только на последнем слое, я не уверен, что вы использовали раньше, но предыдущие слои не нужно было менять абсолютно (не то чтобы использование сигмоида в полностью подключенной сети обязательно неправильно). Однако все эти значения находятся в диапазоне [0, 1], они выглядят правильно. Вы получаете [0.00, 0.90, 0.00, 0.03, 0.40] Похоже, он сильно предсказывает 2-й класс и немного не уверен в 5-м классе. Эти значения не будут нормализованы до 1, каждое значение будет варьироваться в диапазоне [0, 1] независимо, и его можно примерно считать достоверностью для каждого класса. - person David Parks; 22.01.2018