生成式人工智能探索与应用

释放双眼,带上耳机,听听看~!
探索AIGC的生成内容特征,并介绍循环神经网络在文本序列化中的应用,为了更好理解,通过动态图形象地解释了相关概念。

AIGC叫“生成式人工智能”。想必“人工智能”大家都很熟悉了,因此我想跟大伙儿聊聊这个“生成式”。

一、藏头诗展示

AIGC中的GC不是指“国粹”,是Generated Content的缩写,表示生成内容。看下面这段文本:

掘井须到头 金刀剪紫绒 社日放歌还 区区趁适来
掘井须到南 金钿坠芳草 社会前年别 区区趁试期
掘井须到三 金钏色已歇 社客加笾食 区区趁试肠
掘井似翻罗 金钗已十年 社日穿痕月 区区趁试侣

怎么样?是不是有种《诗经》中“回旋往复,一唱三叹”的韵味。

其实,这每一行都是藏头诗。你仔细看看,第一个字连起来就是:掘金社区

上面的内容,是我用AI生成的。

下面的内容,我将从原理到操作,讲一下如何不调API,自己动手写这么个同款AIGC

二、文本序列化

我们先来做一个小游戏。往往小游戏里蕴含着大道理。

看这么一组数据:

序列1 序列2 序列3 序列4 序列5 序列6
1 2 4 8 16

请问,序列6的值是多少?

答案是32!解题思路:后一个数的值是前一个数的2倍。

我还可以搞更多的数据组,让你去探索。比如:

  • 后一个数是前面所有数之和。
  • 后一个数是前面2个数的3倍减去22。

即便实在是找不到规律了。比如就是一串毫无规律的乱码。请问它的后面该是什么?将这串乱码再重复循环,也算是规律。

好了。到这里我想给大家引入一个概念叫做序列

文字领域它有一个特点,那就是具有序列(sequence)特征。

我每天都访问掘金社区,从这里我能获取很多关于___的知识。

空白处该填什么呢?填“备孕”肯定不合适。你可以填“前端”、“人工智能”、“互联网技术”。

哎!你为什么会这么想?因为“掘金社区”是一个IT开发者社区。这句话前边的前提,会影响后边的判断,这就是序列的特点。

那位吃冰糕的先生说了,数字序列的规律我能看出来,但是文字的规律怎么看呢?

其实,我们可以把文字转为数字。

序列1 序列2 序列3 序列4 序列5
文字
数字 0 1 2 3 4

这样转换后,春晓可用04表示,它俩是等价的。

既然我们可以分辨出优雅的诗句与普通的俗语,这说明它是有特征和规律的。

因此,我们可以研究这些序列化后的文本,找找它们前后之间的联系和规律。

那么问题来了,有没有一个好的方案去解决这个问题呢?

三、循环神经网络

小弟我上一篇讲了CNN卷积神经网络,它主要解决图像领域的问题。但是,到了文字领域,CNN就怂了。因为它全靠凑数,没有时间序列这条线。

为了处理具有顺序特征的数据,就出现了循环神经网络(Recurrent Neural Network)也就是RNN。RNN的重点在R(Recurrent 循环)上。它可以将上一个时间步骤的状态,作为当前时间步骤的输入,从而捕捉到序列中的时间依赖关系,并利用先前的状态来影响当前状态的更新。

我的话让你感觉很抽象,你也很想抽我。我发一张图给大家缓解一下。

RNN的结构图一般是这样的:

生成式人工智能探索与应用

这张图解释了RNN是如何循环的。X是输入,A是神经单元,h是输出。输入、输出都带着时间t。这个小单元,他就左手倒右手,转着圈玩儿。凡是经他手的数据,它都会记下一些特征,然后共享这些数据。因此,它就有了记忆。

今天大家来到我的直播间……呸,来到我的掘金专栏,我给大家来点更生动的。

我的读者多是初学者。因此,我们暂且认为RNNLSTMGRU都一样(就像包子、馅饼都是面食)。我不去讲它们的演变史(我讨厌技术课变为历史课)。

为了便于大家理解,我特意画了下面这组动态图。

在某个序列t时,将数据X输入。这个输入数据的格式,后边会重点讲。在这里,大家要了解数据是依次导入的,也就是一条数据转一个单元。

生成式人工智能探索与应用

再看下图,这里演示了单元内部细节。X·t是和h·t-1一起进入的。如果X·t这个时刻代表“春眠不觉晓”的“眠”,那么h·t-1里肯定也包含了“春”的记忆信息。

生成式人工智能探索与应用

下面是它的输出示意图。输入X结合了h·t-1,又输出了新的h·t供后面使用。

生成式人工智能探索与应用

RNN就是这样运作的。循环、递归、记忆传递是它的主题。

好了。道理就讲到这里,再多说,你就要关页面了。下面开干,写代码!

四、实践项目:AI藏头诗

  • 项目名称:利用RNN实现文本生成,写藏头诗。

  • 运行环境:Python 3.9TensorFlow 2.6

  • 代码来源:ChatGPT

既然本文讲AIGC,那么我们就要体现出它的妙用。我向ChatGPT咨询如何实现我的想法,它给出了答案。

生成式人工智能探索与应用

作为一个专业演员……人员。我负责地告诉你,它的回答,步骤明确、完全无误,在数据集完备的情况下,可以直接运行。

这说明了什么?

  • 第一,人工智能的初级应用并不遥远,也并非被束之高阁无法企及。
  • 第二,简单的需求真的是马路边上的东西,但是不为人所知,因为没有人去传播。

下面,我就拿老Chat的代码,按照我的思路,逐一讲解并实现。

4.1 搭建神经网络

刚刚学了RNN,那么我们就来组建一套循环神经网络。tensorflow可以通过Sequential来构建网络模型。

import tensorflow as tf

vocab_size = len(vocab)# 词汇表大小
embedding_dim = 256    # 嵌入维度
rnn_units = 1024       # RNN单元数量
batch_size = 32        # 批次大小

# 构建一个模型
model = tf.keras.Sequential([
    tf.keras.layers.Embedding(vocab_size, embedding_dim,
                              batch_input_shape=[batch_size, None]),
    tf.keras.layers.GRU(rnn_units,
                        return_sequences=True,
                        stateful=True,
                        recurrent_initializer='glorot_uniform'),
    tf.keras.layers.Dense(vocab_size)
])

上面这段代码是整个例子的核心部件。其上游代码为它提供弹药,下游的代码则分析它的产出。

4.1.1 网络结构图解

这个结构很简单,Sequential中就三个层:Embedding作为数据输入、GRU作为神经单元处理、Dense作为结果输出。

下图拿生成诗句举例,对每层做剖析。

生成式人工智能探索与应用

中间是我们熟悉的RNN层,虽然它叫GRU,但它属于RNN家族。代码中参数rnn_units是神经单元的数量,这个我们可以自己调。

我们上面说那么热闹,其实只说了一个基本单元的运转。到真正干活时,是需要一堆单元来处理一条数据的(上图X要连接多个RNN单元)。其他的参数你记住就可以了。

4.1.2 词汇表和嵌入层

下面说说词汇表和嵌入表。不管输入还是输出,我们都要用到它。正是通过它们才将文本转为数字的。

你想让RNN自动生成,前提是它得有料。词汇库就是这个料。

假设我们拿一堆诗句来训练模型,现在诗句也已经变为了数字。接着,我们要做两件事情:

  • 第一:将数据传入模型,让它找规律(联想1、2、4、8那个游戏,规律是2倍关系)。
  • 第二:给定一个起始值(藏头诗也是给一个字),让模型预测后面的数字(32后面是64,64后面是128)。

为了实现上述内容。我们定义了词汇表,用于文本转数字(眠->1)。

我们又定义了嵌入表,让它对一个字进行多维度的描述。0、1、2作为字的代号还可以。春眠是01,春晓是04。但对于文本生成来说,它的颗粒度太糙了。凭这,想要找出文学中深层次的内在规律,根本不可能。还是得将这个字拆成粉末,转为几百维度的向量靠谱。

每个字的向量具体是多少,你肯定不知道。我们正是想让神经网络自己去找(反向传播算法)。于是,我们只下定义就行。因此,我们看到代码中出现了embedding_dim = 256,这是一个有256个维度的嵌入向量。

4.1.3 Dense层的大小

我看看还有啥值得说的。哦!Dense(vocab_size)表示输出层的大小是词汇表的大小。意思就是说,它推测出来的下一个字,是词汇表中的某一个字。

读到这里,你再回头看看上面的图,基本上就恍然大悟了。

好了,大脑已经完成了,下面就让我们去写无脑的代码吧。

4.2 处理训练数据集

既然想要生成藏头诗,那么首先得有一些诗句作为引子。

我自己攒了一些五言古诗+五言对联的句子。有多好不敢说,反正文绉绉的,大约15万条。

生成式人工智能探索与应用

4.2.1 读入文件内容

首先读入这个文件,并生成词汇表。

# 读取文件 
with open('poetry.txt', 'r', encoding='utf-8') as file:
    text = file.read()
# 去除重复字符并排序
vocab = sorted(set(text))
# 创建字符到索引的映射字典
char2idx = {char: idx for idx, char in enumerate(vocab)}

poetry.txt就是存放五言句子的文件。这个文件连同源码,我会上传到Github,链接会附在文末。

建议大家使用jupyter工具,可以逐步执行,查看执行情况。
生成式人工智能探索与应用

上面的代码,text读取了文本内容,vocab是文中所有出现过的字,做了去重和排序处理。这就是我们的词汇库。有了它,文字就可以变为数字了。

text = "春眠不觉晓 处处闻啼鸟 夜来风雨声 花落知多少 ……"
vocab = [' ', '一', '丁', '七', '万', '丈', '三', ……]

4.2.2 整理并序列化数据集

下一步,我们开始组织训练用的数据集。

# 将文本切分为5字一组的数组
sentences = text.split(" ")
x_sequences = []
y_sequences = []
# 组织出训练集(输入+输出)
for sentence in sentences:
    x = [char2idx[char] for char in sentence[:-1]]
    y = [char2idx[char] for char in sentence[1:]]
    x_sequences.append(x)
    y_sequences.append(y)
# 转为tensor数据
datasets = tf.data.Dataset.from_tensor_slices((x_sequences, y_sequences))
# 将数据批次化
datasets = datasets.batch(batch_size, drop_remainder=True)

上面多是常规操作,我着重讲一下输入和输出的制作。

我们期望模型能达到较高的认知水平。举个例子,我们起一个头叫“春”,模型会生成下一个字是“眠”。以此类推,训练集的输入和输出总是会前后错开一个字。

生成式人工智能探索与应用

这就是为什么输入sentence[:-1]不要最后一个字,输出取sentence[1:]不要第一个字。我们期望模型按照这样的思路去探索规律。

4.3 训练模型,保存权重

训练的代码其实很简单:

# 定义损失函数
def loss(labels, logits):
    return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
model.compile(optimizer='adam', loss=loss)

# 训练回调,模型保存路径
callback=tf.keras.callbacks.ModelCheckpoint(filepath="tf2/checkpoint", save_weights_only=True)
# 进行训练
model.fit(datasets, epochs=100, callbacks=[callback])

只要对模型做简单的配置,然后调用model.fit就可以训练了。

  • model是我们一开始通过Sequential创建的。
  • datasets是上一步组的数据集。
  • epochs=100是训练轮次。1个epochs表示把所有数据跑一遍。
  • 训练的最终模型将会保存为tf2/checkpoint

这15万数据,对于普通电脑来说,压力很大。我设置的100epochs才跑了40,就花费了3个多小时。模型的效果,就是文章开头的那样。

其实,大家在学习的时候,可以适当地减少数据量或者epochs来提高验证效率。

训练过程会有如下打印:

Epoch 1/100
4600/4600 [====================] - loss: 5.8752
Epoch 2/100
4600/4600 [====================] - loss: 5.4711

每个Epoch结束,都会有权重保存下来。

4.4 验证模型,生成藏头诗

正常情况下,训练完了。你只要给模型起个头,后面它就会滔滔不绝地输出。

# 给定1个字,变成5个字的方法
def generate_text(model, start_string):
    num_generate = 4   # 生成的字符数
    # 起始字序列化变为数字
    input_eval = [char2idx[s] for s in start_string]
    input_eval = tf.expand_dims(input_eval, 0)
    text_generated = [] # 存放生成的字符
    model.reset_states() # 清空本轮记忆
    # 循环4次,每次生成一个字
    for i in range(num_generate): 
        predictions = model(input_eval)
        predictions = tf.squeeze(predictions, 0)
        # 预测最可信答案的序号
        predicted_id = tf.random.categorical(predictions, num_samples=1)[-1, 0].numpy()
        # 把预测到的字作为输入,继续往下传递
        input_eval = tf.expand_dims([predicted_id], 0)
        # 根据编号找到字符,存起来
        text_generated.append(idx2char[predicted_id])
    return (start_string + ''.join(text_generated))

代码注释比较全面了。

  • start_string是起始字符。就是藏头诗的第一个字。
  • model依然是我们建立的那个RNN序列模型。如果要加载训练好权重,可以调用model.load_weights("tf2/checkpoint")

我们实验一下效果:

generate_text(model, "请")
请以端溪润 
generate_text(model, "嫁")
嫁来贤妻喜 
generate_text(model, "给")
给园支遁隐 
generate_text(model, "我")
我欲掣曳箭

咋说呢?我们不是专业的诗人。但是,单看这个词汇与风格,唬人应该不成问题。

五、结语

说正经的,这用的是古诗数据,它会生成古诗。如果提供别的呢?

我希望本文能给诸位起一个头,让它四处散播,然后在各个行业与领域,生根发芽。

整体项目源码地址(含数据集):github.com/hlwgy/jueji…

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

猴子的洗澡

2023-11-20 8:55:14

AI教程

外卖场景智能陪伴型导购技术探索

2023-11-20 9:04:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索