{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 生成网络\n",
    "\n",
    "循环神经网络（RNN）及其门控单元变体（如长短期记忆单元 LSTM 和门控循环单元 GRU）提供了一种语言建模的机制，即它们可以学习单词的顺序，并预测序列中下一个单词。这使得我们可以将 RNN 用于**生成任务**，例如普通文本生成、机器翻译，甚至图像描述生成。\n",
    "\n",
    "在我们上一单元讨论的 RNN 架构中，每个 RNN 单元会生成下一个隐藏状态作为输出。然而，我们也可以为每个循环单元添加另一个输出，这样就可以输出一个**序列**（其长度等于原始序列的长度）。此外，我们还可以使用不在每一步接受输入的 RNN 单元，而是仅接受一个初始状态向量，然后生成一个输出序列。\n",
    "\n",
    "在本笔记中，我们将专注于帮助我们生成文本的简单生成模型。为了简化起见，让我们构建一个**字符级网络**，逐字母生成文本。在训练过程中，我们需要获取一些文本语料库，并将其拆分为字母序列。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import tensorflow_datasets as tfds\n",
    "import numpy as np\n",
    "\n",
    "ds_train, ds_test = tfds.load('ag_news_subset').values()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 构建字符词汇表\n",
    "\n",
    "为了构建字符级生成网络，我们需要将文本拆分为单个字符，而不是单词。我们之前使用的 `TextVectorization` 层无法实现这一点，因此我们有两个选择：\n",
    "\n",
    "* 手动加载文本并自行进行分词，就像 [这个官方 Keras 示例](https://keras.io/examples/generative/lstm_character_level_text_generation/) 中所展示的那样\n",
    "* 使用 `Tokenizer` 类进行字符级分词\n",
    "\n",
    "我们将选择第二种方法。`Tokenizer` 也可以用于按单词分词，因此可以很轻松地从字符级分词切换到单词级分词。\n",
    "\n",
    "要进行字符级分词，我们需要传递参数 `char_level=True`：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def extract_text(x):\n",
    "    return x['title']+' '+x['description']\n",
    "\n",
    "def tupelize(x):\n",
    "    return (extract_text(x),x['label'])\n",
    "\n",
    "tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
    "tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们还希望使用一个特殊的标记来表示**序列结束**，我们将其称为`<eos>`。让我们手动将其添加到词汇表中：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "eos_token = len(tokenizer.word_index)+1\n",
    "tokenizer.word_index['<eos>'] = eos_token\n",
    "\n",
    "vocab_size = eos_token + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，要将文本编码为数字序列，我们可以使用：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tokenizer.texts_to_sequences(['Hello, world!'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练生成式 RNN 来生成标题\n",
    "\n",
    "我们将通过以下方式训练 RNN 来生成新闻标题。在每一步中，我们会选取一个标题，将其输入到 RNN 中，并让网络根据每个输入字符生成下一个输出字符：\n",
    "\n",
    "![展示 RNN 生成单词 'HELLO' 的示例图像。](../../../../../lessons/5-NLP/17-GenerativeNetworks/images/rnn-generate.png)\n",
    "\n",
    "对于序列的最后一个字符，我们会让网络生成 `<eos>` 标记。\n",
    "\n",
    "我们这里使用的生成式 RNN 的主要区别在于，我们会从 RNN 的每一步输出中获取结果，而不仅仅是从最后一个单元获取。这可以通过为 RNN 单元指定 `return_sequences` 参数来实现。\n",
    "\n",
    "因此，在训练过程中，网络的输入将是某个长度的编码字符序列，输出将是相同长度的序列，但会向后偏移一个元素并以 `<eos>` 结束。小批量数据将由多个这样的序列组成，我们需要使用**填充**来对齐所有序列。\n",
    "\n",
    "接下来，我们将创建一些函数来转换数据集。由于我们希望在小批量级别对序列进行填充，我们会先通过调用 `.batch()` 对数据集进行分批处理，然后使用 `map` 来进行转换。因此，转换函数将以整个小批量数据作为参数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def title_batch(x):\n",
    "    x = [t.numpy().decode('utf-8') for t in x]\n",
    "    z = tokenizer.texts_to_sequences(x)\n",
    "    z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
    "    return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在这里做的一些重要事情：\n",
    "* 我们首先从字符串张量中提取实际文本\n",
    "* `text_to_sequences` 将字符串列表转换为整数张量列表\n",
    "* `pad_sequences` 将这些张量填充到它们的最大长度\n",
    "* 最后，我们对所有字符进行独热编码，同时进行偏移和添加 `<eos>`。我们很快会看到为什么需要独热编码的字符\n",
    "\n",
    "然而，这个函数是 **Pythonic** 的，也就是说，它无法自动转换为 Tensorflow 的计算图。如果我们尝试直接在 `Dataset.map` 函数中使用这个函数，会出现错误。我们需要通过使用 `py_function` 包装器来封装这个 Pythonic 调用：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def title_batch_fn(x):\n",
    "    x = x['title']\n",
    "    a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
    "    return a,b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **注意**: 区分 Pythonic 和 Tensorflow 的数据转换函数可能看起来有些复杂，你可能会疑惑为什么我们不在将数据集传递给 `fit` 之前，使用标准的 Python 函数进行转换。虽然这样确实是可行的，但使用 `Dataset.map` 有一个巨大的优势，因为数据转换管道是通过 Tensorflow 的计算图执行的，这可以利用 GPU 的计算能力，并最大限度地减少在 CPU 和 GPU 之间传递数据的需求。\n",
    "\n",
    "现在我们可以构建生成器网络并开始训练了。它可以基于我们在上一单元中讨论的任何一种循环单元（简单的、LSTM 或 GRU）。在我们的示例中，我们将使用 LSTM。\n",
    "\n",
    "由于网络以字符作为输入，并且词汇表的大小相对较小，我们不需要嵌入层，独热编码的输入可以直接传递到 LSTM 单元中。输出层将是一个 `Dense` 分类器，用于将 LSTM 的输出转换为独热编码的标记编号。\n",
    "\n",
    "此外，由于我们处理的是可变长度的序列，我们可以使用 `Masking` 层来创建一个掩码，从而忽略字符串中填充的部分。这并不是严格必要的，因为我们对 `<eos>` 标记之后的内容并不特别感兴趣，但为了熟悉这种层类型的使用，我们还是会用它。`input_shape` 将是 `(None, vocab_size)`，其中 `None` 表示可变长度的序列，而输出形状同样是 `(None, vocab_size)`，正如你可以从 `summary` 中看到的那样：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"sequential\"\n",
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "masking (Masking)            (None, None, 84)          0         \n",
      "_________________________________________________________________\n",
      "lstm (LSTM)                  (None, None, 128)         109056    \n",
      "_________________________________________________________________\n",
      "dense (Dense)                (None, None, 84)          10836     \n",
      "=================================================================\n",
      "Total params: 119,892\n",
      "Trainable params: 119,892\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = keras.models.Sequential([\n",
    "    keras.layers.Masking(input_shape=(None,vocab_size)),\n",
    "    keras.layers.LSTM(128,return_sequences=True),\n",
    "    keras.layers.Dense(vocab_size,activation='softmax')\n",
    "])\n",
    "\n",
    "model.summary()\n",
    "model.compile(loss='categorical_crossentropy')\n",
    "\n",
    "model.fit(ds_train.batch(8).map(title_batch_fn))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 生成输出\n",
    "\n",
    "现在我们已经训练了模型，接下来我们希望使用它生成一些输出。首先，我们需要一种方法来解码由一系列标记数字表示的文本。为此，我们可以使用 `tokenizer.sequences_to_texts` 函数；然而，它在字符级标记化时效果不佳。因此，我们将从标记器中获取一个标记字典（称为 `word_index`），构建一个反向映射，并编写我们自己的解码函数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
    "\n",
    "def decode(x):\n",
    "    return ''.join([reverse_map[t] for t in x])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们开始生成。我们将从某个字符串 `start` 开始，将其编码为一个序列 `inp`，然后在每一步调用我们的网络来推断下一个字符。\n",
    "\n",
    "网络的输出 `out` 是一个包含 `vocab_size` 元素的向量，表示每个标记的概率。我们可以通过使用 `argmax` 找到最可能的标记编号。然后将这个字符添加到生成的标记列表中，并继续生成。这个生成一个字符的过程会重复 `size` 次，以生成所需数量的字符。如果在生成过程中遇到 `eos_token`，我们会提前终止。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def generate(model,size=100,start='Today '):\n",
    "        inp = tokenizer.texts_to_sequences([start])[0]\n",
    "        chars = inp\n",
    "        for i in range(size):\n",
    "            out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
    "            nc = tf.argmax(out)\n",
    "            if nc==eos_token:\n",
    "                break\n",
    "            chars.append(nc.numpy())\n",
    "            inp = inp+[nc]\n",
    "        return decode(chars)\n",
    "    \n",
    "generate(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在训练过程中采样输出\n",
    "\n",
    "由于我们没有像*准确率*这样的有用指标，判断模型是否在变得更好的唯一方法是通过在训练过程中**采样**生成的字符串。为此，我们将使用**回调函数**，即可以传递给`fit`函数的函数，这些函数会在训练过程中定期被调用。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/3\n",
      "15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
      "Today #39;s a lead in the company for the strike\n",
      "Epoch 2/3\n",
      "15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
      "Today #39;s the Market Service on Security Start (AP)\n",
      "Epoch 3/3\n",
      "15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
      "Today #39;s a line on the strike to start for the start\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sampling_callback = keras.callbacks.LambdaCallback(\n",
    "  on_epoch_end = lambda batch, logs: print(generate(model))\n",
    ")\n",
    "\n",
    "model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个示例已经生成了一些相当不错的文本，但仍有多种方式可以进一步改进：\n",
    "\n",
    "* **更多文本**。我们仅使用了标题来完成任务，但你可能想尝试使用完整的文本。请记住，RNN在处理长序列时表现并不太好，因此可以将它们拆分成较短的句子，或者始终在固定的序列长度（例如，`num_chars`，假设为256）上进行训练。你可以尝试将上面的示例改造成这样的架构，并参考[官方 Keras 教程](https://keras.io/examples/generative/lstm_character_level_text_generation/)来获得灵感。\n",
    "\n",
    "* **多层 LSTM**。尝试使用2或3层的LSTM单元是有意义的。正如我们在前一个单元中提到的，每一层LSTM都会从文本中提取某些模式，而在字符级生成器的情况下，我们可以预期较低的LSTM层负责提取音节，而较高的层负责提取单词和词组组合。这可以通过向LSTM构造函数传递层数参数来简单实现。\n",
    "\n",
    "* 你可能还想尝试使用**GRU单元**，看看哪种表现更好，以及尝试**不同的隐藏层大小**。隐藏层过大可能导致过拟合（例如，网络会学习到精确的文本），而较小的隐藏层可能无法生成良好的结果。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 软文本生成与温度\n",
    "\n",
    "在之前对 `generate` 的定义中，我们总是选择概率最高的字符作为生成文本中的下一个字符。这导致文本经常在相同的字符序列之间反复“循环”，如下例所示：\n",
    "```\n",
    "today of the second the company and a second the company ...\n",
    "```\n",
    "\n",
    "然而，如果我们查看下一个字符的概率分布，可能会发现几个最高概率之间的差距并不大，例如，一个字符的概率是 0.2，另一个是 0.19，等等。例如，当寻找序列 '*play*' 的下一个字符时，下一个字符可能同样可能是空格，或者是 **e**（如单词 *player* 中）。\n",
    "\n",
    "这让我们得出结论，选择概率较高的字符并不总是“公平”的，因为选择第二高的字符也可能生成有意义的文本。更明智的做法是从网络输出给出的概率分布中**采样**字符。\n",
    "\n",
    "这种采样可以通过 `np.multinomial` 函数完成，该函数实现了所谓的**多项分布**。下面定义了一个实现这种**软**文本生成的函数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "--- Temperature = 0.3\n",
      "Today #39;s strike #39; to start at the store return\n",
      "On Sunday PO to Be Data Profit Up (Reuters)\n",
      "Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
      "President olding of the blast start for the strike to pay &lt;b&gt;...&lt;/b&gt;\n",
      "Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\n",
      "\n",
      "--- Temperature = 0.8\n",
      "Today countie strikes ryder missile faces food market blut\n",
      "On Sunday collores lose-toppy of sale of Bullment in &lt;b&gt;...&lt;/b&gt;\n",
      "Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n",
      "President Ol Luster for Profit Peaced Raised (AP)\n",
      "Little red riding hood dace on depart talks #39; bank up\n",
      "\n",
      "--- Temperature = 1.0\n",
      "Today wits House buiting debate fixes #39; supervice stake again\n",
      "On Sunday arling digital poaching In for level\n",
      "Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n",
      "President teps help of roubler stepted lessabul-Dhalitics (AFP)\n",
      "Little red riding hood signs on cash in Carter-youb\n",
      "\n",
      "--- Temperature = 1.3\n",
      "Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n",
      "On Sunday hround elitwing wint EU Powerburlinetien\n",
      "Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n",
      "President lost securitys from power Elections in Smiltrials\n",
      "Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n",
      "\n",
      "--- Temperature = 1.8\n",
      "Today #39;It: He deat: N.KA Asside\n",
      "On Sunday i arry Par aldeup patient Wo stele1\n"
     ]
    },
    {
     "ename": "KeyError",
     "evalue": "0",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     18\u001b[0m     \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     19\u001b[0m     \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m     11\u001b[0m             \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m             \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m: 0"
     ]
    }
   ],
   "source": [
    "def generate_soft(model,size=100,start='Today ',temperature=1.0):\n",
    "        inp = tokenizer.texts_to_sequences([start])[0]\n",
    "        chars = inp\n",
    "        for i in range(size):\n",
    "            out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
    "            probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n",
    "            probs = probs/np.sum(probs)\n",
    "            nc = np.argmax(np.random.multinomial(1,probs,1))\n",
    "            if nc==eos_token:\n",
    "                break\n",
    "            chars.append(nc)\n",
    "            inp = inp+[nc]\n",
    "        return decode(chars)\n",
    "\n",
    "words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n",
    "    \n",
    "for i in [0.3,0.8,1.0,1.3,1.8]:\n",
    "    print(f\"\\n--- Temperature = {i}\")\n",
    "    for j in range(5):\n",
    "        print(generate_soft(model,size=300,start=words[j],temperature=i))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们引入了一个名为**温度**的参数，用于指示我们应多大程度上坚持最高概率。如果温度为1.0，我们进行公平的多项式采样，而当温度趋于无穷大时——所有概率变得相等，我们随机选择下一个字符。在下面的示例中，我们可以观察到，当我们将温度增加得过高时，文本变得毫无意义，而当温度接近0时，它类似于“循环”的硬生成文本。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n---\n\n**免责声明**：  \n本文档使用AI翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 进行翻译。尽管我们努力确保翻译的准确性，但请注意，自动翻译可能包含错误或不准确之处。应以原始语言的文档作为权威来源。对于关键信息，建议使用专业人工翻译。我们对因使用此翻译而引起的任何误解或误读不承担责任。\n"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
  },
  "kernelspec": {
   "display_name": "Python 3.8.12 ('py38')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "coopTranslator": {
   "original_hash": "9fbb7d5fda708537649f71f5f646fcde",
   "translation_date": "2025-08-31T10:32:32+00:00",
   "source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
   "language_code": "zh"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}