Время прочтения: 4 мин.

Обучать сеть буду таким образом, чтобы в результате она могла определить принадлежность текста автору, при решении задачи буду опираться на понятие авторского инварианта. Исходные данные есть, так как в предыдущей публикации я достал стихи поэтов-классиков с culture.ru.

Авторским инвариантом называют некоторый параметр, который отражает уникальный «стиль» автора или нескольких авторов.

Задачей определения автора текста называют процесс сравнения наиболее характерных особенностей стилистики текстов.

Обязательные свойства стилистической характеристики:

  1. Используется в большем количестве текстов автора.
  2. Позволяет чётко отнести автора к той или иной группе.

Примеры стилистических характеристик: частоты использования определённых слов в предложении и определённых частей речи, количество и длина слогов.

Используемые модули:

import pandas as pd
import os
import tensorflow as tf
import matplotlib.pyplot as plt
from IPython.display import clear_output 

Проведу предобработку текстов: удалю лишние символы и разделю набор текстов на обучающую и тестовую выборки:

# чтение .json файлов
path = "./ready_data"
for filename in os.listdir(path):
    if ".json" in filename:
        name = filename[:filename.find(".")].replace("author-", "")
        print(name)
        df = pd.read_json(path + os.sep + filename, orient="index")
        df["text"] = (df["text"].str.findall('([А-я ]*)').str.join(" ")).apply(deadSpace)
        df.to_excel(f"./xlsx/{name}.xlsx")
        clear_output()

# разделение выборки
train = pd.DataFrame()
test = pd.DataFrame()
tr = 0.7
it = 0
for k, v in maxLenAuthors.items():
    auth = pd.read_excel(path + os.sep + k)
    auth["author"] = k[:k.find(".")]
    auth["Y"] = it
    train = pd.concat([train, auth[:int(len(auth) * tr)]])
    test = pd.concat([test, auth[int(len(auth) * tr):]])
    it += 1
train.to_excel("./train.xlsx")
test.to_excel("./test.xlsx")
train = pd.read_excel("./train.xlsx")
test = pd.read_excel("./test.xlsx")
train_text = train["text"]	
train_y = tf.keras.utils.to_categorical(train["Y"])

Токенизирую тексты, таким образом приведу к частотному представлению:

# выбор параметров токенизатора
max_words = 10000
max_len = 30
tokenizer = tf.keras.preprocessing.text.Tokenizer(max_words)
tokenizer.fit_on_texts(train_text)

Выведу результат визуализации частотного представления текста:

# преобразование текста в последовательность и визуализация результата
seq = tokenizer.texts_to_sequences(train_text)
index = 1
print(train_text[index])
print(seq[index])
print(len(seq[index]) == len(train_text[index]))
x_train = tf.keras.preprocessing.sequence.pad_sequences(seq, maxlen=max_len)

Создам и обучу модель, построю график визуализации изменения количества верных ответов с увеличением числа прошедших эпох обучения:

# создание экземпляра модели
model_cnn = tf.keras.models.Sequential()
# выбор слоёв и их параметров
# Embedding для перехода от последовательностей к векторному представлению
model_cnn.add(tf.keras.layers.Embedding(max_words, 32, input_length=max_len))
# Conv1D для операции свёртки
model_cnn.add(tf.keras.layers.Conv1D(250, 5, padding='valid', activation='relu'))
# GlobalMaxPooling1D для отбора параметров имеющих больший вклад в результат
model_cnn.add(tf.keras.layers.GlobalMaxPooling1D())
# Dense с l1 для получения только значимых параметров
model_cnn.add(tf.keras.layers.Dense(128, activation='relu', activity_regularizer = tf.keras.regularizers.L1L2(l1=0.0001, l2=0)))
# Dense с softmax для получения результата с вероятностным распределением
model_cnn.add(tf.keras.layers.Dense(numAuthors, activation='softmax'))
# выбор параметров обучения
# adam – градиетный спуск
# categorical_crossentropy – функция потерь, рекомендуемая в документации для multiclass-классификации
# accuracy – метрика (верные положительные ответы + верные отрицательные) / множество ответов
model_cnn.compile(optimizer='adam', 
              loss='categorical_crossentropy', 
              metrics=['accuracy'])
# файл в который будет сохранена модель
model_cnn_save_path = 'best_model_cnn.h5'
# ранняя остановка обучения
checkpoint_callback_cnn = tf.keras.callbacks.ModelCheckpoint(model_cnn_save_path, 
                                      monitor='val_accuracy',
                                      save_best_only=True,
                                      verbose=1)
# обучение
history_cnn = model_cnn.fit(x_train, 
                            train_y, 
                            epochs=5,
                            batch_size=100,
                            validation_split=0.3,
                            callbacks=[checkpoint_callback_cnn])

Предобработаю и предскажу несколько текстов из тестовой выборки:

# преобразование текста в последовательность
test_sequences = tokenizer.texts_to_sequences(test['text'])
x_test = tf.keras.preprocessing.sequence.pad_sequences(test_sequences, maxlen=max_len)
test_y = tf.keras.utils.to_categorical(test["Y"])
# загрузка модели из файла
model_cnn.load_weights(model_cnn_save_path)
# предсказание 1
n1 = 678
n2 = n1 + 1
# вывод текста, предсказания и истинных значений 
tokenizer.sequences_to_texts(x_test[n1:n2]), model_cnn.predict(x_test[n1:n2]), test_y[n1], maxLenAuthors.keys()

Беру индекс большего значения из вывода модели и соотношу с массивом авторов, в данном случае автор текста Игорь Северянин.

# предсказание 2
n1 = 0
n2 = n1 + 1
tokenizer.sequences_to_texts(x_test[n1:n2]), model_cnn.predict(x_test[n1:n2]), test_y[n1], maxLenAuthors.keys()

Беру индекс большего значения из вывода модели и соотношу с массивом авторов, в данном случае автор текста Валерий Брюсов.

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

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