Вложения слов для TensorFlow NMT

TensorFlow глубокое обучение
Вложения слов для TensorFlow NMT

Заявление: эта статья была первоначально создана Луо Чжоуяном глупым me.me.lzy@gmail.com и не может быть использована в коммерческих целях без разрешения

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

Что такое встраивание?

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

Разве не легко преобразовать слова в числа? Самое простое, для каждого слова мы даем целое число для представления, разве этого недостаточно? Далее, для каждого слова мы задаем вектор фиксированной длины, пусть некоторая позиция (которую можно представить предшествующим целым числом) делает значение этой позиции равной 1, а остальные позиции равны 0. То есть, если у нас есть 1000 слов, то мы запишем каждое слово как вектор-столбец из элементов 1000. В каждом векторе только одна позиция имеет значение 1, а остальные позиции равны 0, например:

  • привет --> [1,0,0,0,…,0]

  • мир --> [0,1,0,0,…,0]

На самом деле приведенная выше кодировкаone-hotкодировка, переводится какоднократное кодирование.

Но наш Embedding этого не делает, почему? Основная причина указана вышеone-hotКодирование имеет следующие серьезные недостатки:

  • Взрыв размерности, если у меня есть 300 000 слов, каждое слово нуждается в векторном представлении [1,300000]. Чем больше слов, тем выше размерность

  • Связь между словами не может быть выражена.

То есть нашEmbeddingНужно решить вышеуказанные проблемы, то как это сделать? Тоже очень просто:

  • Для каждого слова мы используем вектор фиксированной длины для его представления, например, длину 256.

  • Для каждого представления вместо использования представления 0 или 1 мы используем числа с плавающей запятой, и каждое значение вектора может быть числом с плавающей запятой.

Таким образом решаются две вышеуказанные проблемы.

На самом деле вы, должно быть, нашли проблему, матрица, составленная из цифрового представления наших слов, все значения могут быть изменены, так как же должно измениться это изменение? Это очень важный вопрос!

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

ответ:Конечно!. Это также может уменьшить количество параметров обучения, тем самым сократив время обучения! Собственно, в проекте tensorflow/nmt есть параметр--embed_prefixОн относится к файлу, в котором сохраняются значения этой так называемой матрицы!

ЭтоEmbeddingВсе секреты вовсе не загадочны, правда?

Процесс построения Embedding в проекте NMT

Запись кода встраивания слова для TensorFlow NMT находится по адресуnmt/model.pyдокумент,BaseModelсуществует одинinit_embeddings()метод, модель NMT завершает здесь инициализацию встраивания слов.

Согласно приведенному выше введению, мы знаем, что существует два способа создания встраивания:

  • Загружать напрямую из уже обученного файла (embed_file)

  • Создайте матрицу и позвольте модели обучаться самостоятельно

Далее мы представим эти два метода по отдельности.tensorflow/nmtПроцесс сборки в проекте.

Получить необходимые параметры из гиперпараметров

Если вам нужно выполнить встраивание слов, вы должны сначала получить необходимую информацию. Например, файлы словарей или файлы встраивания слов (если уже есть обученные файлы встраивания слов). Эта информация передается через параметр гиперпараметра hparams. Основные параметры получаются следующим образом:

 1    def _init_embeddings(self, hparams, scope): 2        # 源数据和目标数据是否使用相同的词典 3        share_vocab = hparams.share_vocab 4        src_vocab_size = self.src_vocab_size 5        tgt_vocab_size = self.tgt_vocab_size 6        # 源数据词嵌入的维度,数值上等于指定的神经单元数量 7        src_embed_size = hparams.num_units 8        # 目标数据词嵌入的维度,数值上等于指定的神经单元数量 9        tgt_embed_size = hparams.num_units10        # 词嵌入分块数量,分布式训练的时候,需要该值大于111        num_partitions = hparams.num_embeddings_partitions12        # 源数据的词典文件13        src_vocab_file = hparams.src_vocab_file14        # 目标数据的词典文件15        tgt_vocab_file = hparams.tgt_vocab_file16        # 源数据已经训练好的词嵌入文件17        src_embed_file = hparams.src_embed_file18        # 目标数据已经训练好的词嵌入文件19        tgt_embed_file = hparams.tgt_embed_file2021        # 分块器,用于分布式训练22        if num_partitions <= 1:23            # 小于等于1,则不需要分块,不使用分布式训练24            partitioner = None25        else:26            # 分块器也是一个张量,其值大小和分块数量一样27            partitioner = tf.fixed_size_partitioner(num_partitions)2829        # 如果使用分布式训练,则不能使用已经训练好的词嵌入文件30        if (src_embed_file or tgt_embed_file) and partitioner:31            raise ValueError(32                "Can't set num_partitions > 1 when using pretrained embedding")скопировать код

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

Создать или загрузить матрицу встраивания слов

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

 1        # 创建词嵌入的变量域 2        with tf.variable_scope(scope or "embeddings", dtype=tf.float32, partitioner=partitioner) as scope: 3            # 如果共享词典 4            if share_vocab: 5                # 检查词典大小是否匹配 6                if src_vocab_size != tgt_vocab_size: 7                    raise ValueError("Share embedding but different src/tgt vocab sizes" 8                                     " %d vs. %d" % (src_vocab_size, tgt_vocab_size)) 9                assert src_embed_size == tgt_embed_size10                vocab_file = src_vocab_file or tgt_vocab_file11                embed_file = src_embed_file or tgt_embed_file12                # 如果有训练好的词嵌入模型,则直接加载,否则创建新的13                embedding_encoder = self._create_or_load_embed(14                    "embedding_share", vocab_file, embed_file,15                    src_vocab_size, src_embed_size, dtype=tf.float32)16                embedding_decoder = embedding_encoder17            # 不共享词典的话,需要根据不同的词典创建对应的编码器和解码器18            else:19                # 加载或者创建编码器20                with tf.variable_scope("encoder", partitioner=partitioner):21                    embedding_encoder = self._create_or_load_embed(22                        "embedding_encoder", src_vocab_file, src_embed_file,23                        src_vocab_size, src_embed_size, tf.float32)24                # 加载或创建解码器25                with tf.variable_scope("decoder", partitioner=partitioner):26                    embedding_decoder = self._create_or_load_embed(27                        "embedding_decoder", tgt_vocab_file, tgt_embed_file,28                        tgt_vocab_size, tgt_embed_size, tf.float32)29            self.embedding_encoder = embedding_encoder30            self.embedding_decoder = embedding_decoderскопировать код

Как вы можете видеть, есть оценка share_vocab, прежде чем получить представление встраивания слова. Это суждение также очень простое: определить, используют ли исходные данные и целевые данные один и тот же словарь, является ли он share_vocab или нет, и, наконец, необходимо создать или загрузить представление встраивания слова. Этот важнейший процесс является_create_or_load_embed()завершено в функции. Основная работа этой функции заключается в следующем:

 1    def _create_or_load_embed(self, embed_name, vocab_file, embed_file, vocab_size, embed_size, dtype=tf.float32): 2        # 如果提供了训练好的词嵌入文件,则直接加载 3        if vocab_file and embed_file: 4            embedding = self._create_pretrained_emb_from_txt(vocab_file, embed_file) 5        else: 6            # 否则创建新的词嵌入 7            with tf.device(self._get_embed_device(vocab_size)): 8                embedding = tf.get_variable( 9                    embed_name, [vocab_size, embed_size], dtype)10        return embeddingскопировать код

Загрузить предварительно обученные представления встраивания слов

Если гиперпараметр предоставляет предварительно обученный файл встраивания слов embed_file, то нам нужно только прочитать файл, создать матрицу встраивания слов и вернуть ее. Основной код выглядит следующим образом:

 1    def _create_pretrained_emb_from_txt(self, vocab_file, embed_file, 2                                        num_trainable_tokens=3, dtype=tf.float32, scope=None): 3        """ 4        从文件加载词嵌入矩阵 5        :param vocab_file: 词典文件 6        :param embed_file: 训练好的词嵌入文件 7        :param num_trainable_tokens:词典文件前3个词标记为变量,默认为"<unk>","<s>","</s>" 8        :param scope: 域 9        :return: 词嵌入矩阵10        """11        # 加载词典12        vocab, _ = vocab_utils.load_vocab(vocab_file)13        # 词典的前三行会加上三个特殊标记,取出三个特殊标记14        trainable_tokens = vocab[:num_trainable_tokens]1516        utils.print_out("# Using pretrained embedding: %s." % embed_file)17        utils.print_out("  with trainable tokens: ")1819        # 加载训练好的词嵌入20        emb_dict, emb_size = vocab_utils.load_embed_txt(embed_file)21        for token in trainable_tokens:22            utils.print_out("    %s" % token)23            # 如果三个标记不在训练好的词嵌入中24            if token not in emb_dict:25                # 初始化三个标记为0.0,维度为词嵌入的维度26                emb_dict[token] = [0.0] * emb_size2728        # 从训练好的词嵌入矩阵中,取出词典中的词语的词嵌入表示,数据类型为tf.float3229        emb_mat = np.array(30            [emb_dict[token] for token in vocab], dtype=dtype.as_numpy_dtype())31        # 常量化词嵌入矩阵32        emb_mat = tf.constant(emb_mat)33        # 从词嵌入矩阵的第4行之后的所有行和列(因为num_trainable_tokens=3)34        # 也就是说取出除了3个标记之外所有的词嵌入表示35        # 这是常量,因为已经训练好了,不需要训练了36        emb_mat_const = tf.slice(emb_mat, [num_trainable_tokens, 0], [-1, -1])37        with tf.variable_scope(scope or "pretrain_embeddings", dtype=dtype) as scope:38            with tf.device(self._get_embed_device(num_trainable_tokens)):39                # 获取3个标记的词嵌入表示,这3个标记的词嵌入是可以变的,通过训练可以学习40                emb_mat_var = tf.get_variable(41                    "emb_mat_var", [num_trainable_tokens, emb_size])42        # 将3个标记的词嵌入和其余单词的词嵌入合并起来,得到完整的单词词嵌入表示43        return tf.concat([emb_mat_var, emb_mat_const], 0)скопировать код

Процесс обработки я очень четко написал в комментариях. Далее рассмотрим процесс создания нового представления встраивания слова.

Воссоздайте представление встраивания слова

Этот процесс на самом деле очень прост, просто создайте обучаемый тензор:

1with tf.device(self._get_embed_device(vocab_size)):2    embedding = tf.get_variable(embed_name, [vocab_size, embed_size], dtype)  скопировать код

Имя тензораembed_name, форма [vocab_size, embed_size], гдеvocab_size- размер словаря, который представляет собой количество строк двумерной матрицы,embed_sizeЭто размерность встраивания слов, сколькими числами представлено каждое слово, то есть количество столбцов двумерной матрицы. Тип данных этого тензора — число с плавающей запятой одинарной точности. Конечно,tf.get_variable()Метод также имеет ряд параметров, предоставляющих значения по умолчанию, один из которыхtrainable=True, что означает, что эта переменная является переменной, то есть наше встраивание слов означает, что число будет меняться в процессе обучения.

На этом процесс подготовки к встраиванию слов завершен.