Переосмысление Плутарха с помощью Tensorflow 2.0

Плутарх "Жизни благородных греков и римлян через вложения слов в TensorFlow 2.0"

Преамбула

Жизни благородных греков и римлян Плутарха, также называемые Параллельные жизни или просто Жизни Плутарха, представляют собой серию биографий знаменитых древних греков. и римляне, от Тесея и Ликурга до Марка Антония.

В недавно опубликованной статье мы рассмотрели возможность обучения нашим собственным вложениям слов с помощью библиотеки gensim. Здесь мы в первую очередь сосредоточимся на слое встраивания слов, использующем платформу TensorFlow 2.0; цель состоит в том, чтобы лучше понять, как работает слой и как он способствует успеху более крупных моделей НЛП.





Чтобы упростить репликацию, я адаптировал код для Google Colab и выделил уникальные особенности этой платформы - в противном случае весь код можно запустить на вашем локальном компьютере с помощью Python 3.6+ и соответствующих пакетов. Код представлен на протяжении всей статьи, но я пропущу некоторые дополнительные или второстепенные коды - весь код можно найти в моем репозитории Github.

Текст, использованный в этом анализе, был предоставлен Проектом Гутенберг.

Настройка вещей

В Colab изменим Runtime Type на GPU, а затем импортируем последнюю версию TensorFlow - этот фрагмент ниже будет работать только в Colab, в противном случае просто используйте команды pip или conda install, чтобы загрузить последнюю версию TensorFlow на свой компьютер.

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

import os
import re
fpath = os.getcwd(); fpath

Давайте импортируем текст (Plutarch.txt) на диск Google Colab - нам нужно иметь в виду, что наши файлы там недолговечны, и нам нужно будет загружать их каждый раз после длительного перерыва в использовании платформы:

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

Давайте прочитаем текст и выполним несколько основных операций с регулярными выражениями:

import re
corpus = open(fpath + '/Plutarch.txt',  'rb').read().lower().decode(encoding='utf-8')
corpus = re.sub('\n', ' ', corpus) #remove new line
corpus = re.sub('\r', ' ', corpus) #remove "return"

Поскольку мы будем разбивать текст на предложения, новая строка не имеет значения для нашего анализа. Кроме того, при использовании текстового токенизатора я заметил, что наличие «\ r» (обозначающего возврат каретки) создает ложные уникальные слова, такие как «мы» и «мы \ r» - опять же, неважно в нашем случае. Следовательно, оба символа «\ n» и «\ r» должны быть удалены.

Создание словаря

По мере того, как мы приближаемся к фактическому встраиванию слов, давайте разберем текст на предложения:

import nltk
from nltk.tokenize import sent_tokenize
nltk.download('punkt') #need in Colab upon resetting the runtime
 
# tokenize at sentence level
sentences = nltk.sent_tokenize(corpus)
print("The number of sentences is {}".format(len(sentences)))

Мы увидим, что в тексте всего 16 989 предложений. Затем нам нужно рассчитать количество слов в самых длинных предложениях - причина станет очевидной позже в уроке:

from nltk.tokenize import word_tokenize
word_count = lambda sentence: len(word_tokenize(sentence))
longest_sentence = max(sentences, key=word_count)
length_longest_sentence = len(word_tokenize(longest_sentence))
print("The longest sentence has {} words".format(length_longest_sentence))

Оказывается, самое длинное предложение состоит из 370 слов. Затем давайте преобразуем весь текст в положительные числа, чтобы мы могли говорить на одном языке с TensorFlow:

Из вышесказанного мы также выясняем, что текст содержит 20241 уникальное слово, поскольку токенизатор присваивает только одно число для одного и того же слова. Чтобы стандартизировать длину всех предложений (т.е. превратить входные данные в один тензор одинаковой формы, чтобы сделать их обрабатываемыми / упрощенными для модели - мы здесь, чтобы удовлетворить потребности машин), нам нужно преобразовать список чисел, представляющих слова (sent_numeric) в фактический словарь (word_index), и добавить заполнение. Мы также можем комбинировать усечение очень длинных предложений с заполнением коротких предложений, но в этом случае мы просто увеличим длину самого длинного предложения.

Размер словарного запаса (он же количество уникальных слов) увеличится на 1 до 20 242 в результате добавления 0 для заполнения. Введите «данные [0]» (т. Е. Первое предложение), чтобы увидеть, как будет выглядеть первое предложение с заполнением.

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

reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
def decode_data(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])

Имеет смысл перепроверить индексирование слов и преобразование - одна ошибка, скорее всего, отбросит весь набор данных и сделает его непонятным. Примеры перекрестной проверки - до и после конвертации - доступны в моем репозитории Github.

Модель

Наконец, давайте построим и запустим модель. TensorFlow предоставляет хороший учебник, который мы адаптируем к нашим потребностям.

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

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

Давайте продолжим и построим новую, очень простую архитектуру модели после импорта соответствующих библиотек:

Слой внедрения, который обычно может использоваться в качестве первого уровня в модели, преобразует последовательности уникальных слов с числовой кодировкой (в качестве напоминания, 20 241 из них плюс заполнение, закодированное как ноль) в последовательности векторов, последние изучаются как модель поезда. Каждый вектор будет иметь 100 измерений (embedding_dim = 100), следовательно, в результате у нас будет матрица 20242 x 100 .. Длина ввода будет фиксирована на длину самого длинного предложения, то есть 370 слов, как каждое отдельное слово. воспринимается моделью как имеющий одинаковый размер из-за заполнения. Mask_zero сообщает модели, является ли входное значение 0 специальным значением заполнения, которое должно быть замаскировано, что особенно полезно в повторяющихся слоях, где модель может обрабатывать переменные входные длины.

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

Вот сводка модели (модель с дополнительным плотным слоем находится в репозитории github):

В сводке модели мы увидим, что количество параметров для слоя внедрения составляет 2 024 200, что на 20 242 слова умножено на размер встраивания, равный 100.

В ранее упомянутом руководстве TensorFlow используется набор данных обзоров, в котором каждый из обзоров имеет отметку 1 или 0 в зависимости от положительного или отрицательного настроения. У нас нет роскошных этикеток, но мы все же хотим протестировать эту модель, поэтому просто создадим массив нулей и прикрепим к каждому из предложений; модель требует такой конструкции. Это будет не первый и не последний раз, когда машинный интеллект сталкивается с неразрешимой задачей, но все же требует от нас решения. Обучим эту модель:

import numpy as np
adam = tf.keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=False) 
model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])
batch_size = 16989  #number of sentences
data_labels = np.zeros([batch_size, 1])
history = model.fit(
    data,
    data_labels,
    epochs=200,
    batch_size=batch_size,
    verbose = 0)

Вложения обучены. Прежде чем перейти к визуализации, давайте быстро проверим сходство слов в gensim. Во-первых, нам нужно создать файл векторов - временно сохранить его в Colab или загрузить на локальный компьютер:

f = open('vectors.tsv' ,'w')
f.write('{} {}\n'.format(vocab_size-1, embedding_dim))
vectors = model.get_weights()[0]
for words, i in tokenizer.word_index.items():
    str_vec = ' '.join(map(str, list(vectors[i, :])))
    f.write('{} {}\n'.format(words, str_vec))
f.close()
# download the file to the local machine by double-clicking the Colab file or using this:
try:
  from google.colab import files
except ImportError:
   pass
else:
  files.download('vectors.tsv')

Во-вторых, давайте

import gensim
w2v = gensim.models.KeyedVectors.load_word2vec_format('./vectors.tsv', binary=False)
w2v.most_similar('rome')

Наконец, давайте проверим сходство между Помпеем и Цезарем, которое показало высокие результаты в модели CBOW, которую мы ранее обучили:

round(w2v.similarity('pompey', 'caesar'),4)

Связь между словами высокая. Кроме того, как и следовало ожидать, Цезарь очень похож на Рим.

Для тех, кто интересуется более сложными моделями, в моем файле Github доступны дополнительные варианты, включая рекуррентные нейронные сети (Long Short-Term Memory), но имейте в виду, что они будут обучаться намного медленнее, чем простая модель выше.

Визуализация

Для визуализации эмбеддингов сложно превзойти проектор TensorFlow, поэтому давайте создадим векторные и мета-файлы (то есть слова, соответствующие этим векторам) для его использования:

Импортируйте файлы локально, а затем мы можем перейти в TensorFlow’s Projector, загрузить файлы, чтобы заменить данные по умолчанию, и попробовать различные варианты, доступные на сайте. Вот вид анализа основных компонентов всего векторного пространства текста:

А вот только векторное пространство для 100 слов, которые оказались наиболее похожими на «Рим».

Вывод

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

Слой внедрения также можно использовать для загрузки предварительно обученных встраиваний слов (например, GloVe, BERT, FastText, ELMo), что, как я считаю, обычно было бы более продуктивным способом использования моделей, требующих таких встраиваний - отчасти из-за «промышленного уровня». ”Усилия и объем данных, необходимые для их создания. Однако в случаях специализированного текста и особенно если корпус, на котором могут быть обучены вложения слов, велик, обучение собственных встраиваний все еще может быть более эффективным.