Время прочтения: 4 мин.
Обучать сеть буду таким образом, чтобы в результате она могла определить принадлежность текста автору, при решении задачи буду опираться на понятие авторского инварианта. Исходные данные есть, так как в предыдущей публикации я достал стихи поэтов-классиков с culture.ru.
Авторским инвариантом называют некоторый параметр, который отражает уникальный «стиль» автора или нескольких авторов.
Задачей определения автора текста называют процесс сравнения наиболее характерных особенностей стилистики текстов.
Обязательные свойства стилистической характеристики:
- Используется в большем количестве текстов автора.
- Позволяет чётко отнести автора к той или иной группе.
Примеры стилистических характеристик: частоты использования определённых слов в предложении и определённых частей речи, количество и длина слогов.
Используемые модули:
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()
Беру индекс большего значения из вывода модели и соотношу с массивом авторов, в данном случае автор текста Валерий Брюсов.
Таким образом, в ходе решения задачи классификации, получена модель с достаточно малым количеством обученных параметров, позволяющая определить принадлежность текста автору с помощью признаков, основанных на частоте, а структурные элементы модели, в момент отнесения текста
к некоторому классу, содержат характерные для автора признаки, наблюдаемые в большом количестве его произведений.
При решении схожей задачи с большим количеством классов или иными требованиями к точности, необходимо провести более тщательную предобработку текстов, подобрать оптимальное количество обучаемых параметров или воспользоваться предобученными моделями.