大家好,我们是 AI . FREE Team - 人工智慧自由团队,这一次的铁人赛,自由团队将从0到1 手把手教各位读者学会 (1)Python基础语法 (2)Python Web 网页开发框架 – Django (3)Python网页爬虫 – 周易解梦网 (4)Tensorflow AI语言模型基础与训练 – LSTM (5)实际部属AI解梦模型到Web框架上。
自由团队的成立宗旨为开发AI/新科技的学习资源,提供各领域的学习者能够跨域学习资料科学,并透过自主学习发展协杠职涯,结合智能应用到各式领域,无论是文、法、商、管、医领域的朋友,都可以自由的学习AI技术。
AI . FREE Team 读者专属福利 → Python Basics 免费学习资源
接下来介绍LSTM的部分,继上次介绍完RNN(Recurrent Neural Network),这次我们就来看一下LSTM的起源与原理
RNN再长篇文章的发挥中,经常会有前期资讯到後面影响的决策影响越小,也就是前面的权重再後期的影响会越来越小的梯度消失问题。
而为了解决这个问题,1997年 LSTM(Long Short-Trem Memory) 首次发表於论文上,其独特的设计可以良好的处理间隔与时序较长的Task,而真正发扬光大是在2009年的ICDAR的手写识别比赛,透过LSTM建立的模型取得冠军。
LSTM虽然看起来很复杂,但是在计算上其实与RNN是有相似之处,都是会透过前一个的输出加上这次计算的输入,不过LSTM特别之处就在有额外3个门闸(gate),会决定input是否重要到能被记住及能不能被输出output。
※ 没有好的数据,演算法也做不出好的决策。
那是因为全世界每天有15亿的人,在帮 google 做决策(整理、准备数据)。
因此,这次的数据,我们就从灵狐算命网爬取资料
text_sentence = []
with open("clean_all.txt","r") as f:
for i in f.readlines():
if i.strip() != '':
text_sentence.append(i.strip())
text = ''.join(text_sentence)
text[:50]
这里的 with open("clean_all.txt","r") as f:
路径因人而异
大家放档案的位置可能都不同,我这里使用的是相对路径
n = len(text)
w = len(set(text))
print(f'这个资料集共有{n}个字')
print(f'这个资料集不重复的字有{w}个字')
当资料准备好也处理完之後,我们要将文本转成数字,因为电脑看的是数字不是文字,这个步骤大家应该都能够理解吧~
numpy
大家肯定最熟悉不过了,他提供了很好的矩阵功能API,不论是在运算或是高阶函式库,都是大家做深度学习时最好的朋友~tensorflow
,tensorflow是google实验室开发的深度学习框架,提供的高阶API可以加速我们完成这个任务tf.keras.preprocessing.text.Tokenizer
,来当作tokenizerimport tensorflow as tf
import numpy as np
# 初始化一个以字为单位的 Tokenizer
tokenizer = tf.keras.preprocessing.text.Tokenizer(
num_words=w,
char_level=True,
filters=''
)
# 将文字转成int
tokenizer.fit_on_texts(text)
text_as_int = tokenizer.texts_to_sequences([text])[0]
print(text_as_int[:10])
len(text_as_int)
index_word
将数值转回文字,并检查start_idx = 10
end_idx = 20
partial_indices = text_as_int[start_idx:end_idx]
partial_texts = [
tokenizer.index_word[idx] for idx in partial_indices
]
print("原本的中文字序列:")
print()
print(partial_texts)
print()
print()
print("转换後的索引序列:")
print()
print(partial_indices)
AI 可以帮我们解决问题,但是 AI 不会知道问题是什麽,问题由我们定义!
_type = type(text_as_int)
n = len(text_as_int)
print(f"text_as_int 是一个 {_type}\n")
print(f"文本的序列长度: {n}\n")
print("前 5 索引:", text_as_int[:5])
print("实际丢给模型的数字序列:")
print(partial_indices[:-1])
print()
print("方便我们理解的文本序列:")
print(partial_texts[:-1])
print("实际丢给模型的文本序列:")
print(partial_texts[:-1])
print()
print("模型预期输出的文本序列:")
print(partial_texts[1:])
tensorflow
的API tf.data.Dataset.from_tensor_slices
,将转换成数值的文本,转成tensorflow
可以接受的tensor
# 将list转换成tensor
characters = tf.data.Dataset.from_tensor_slices(text_as_int)
characters
batch
这个内建的function# tensorflow的dataset
SEQ_LENGTH = 10 # 数字序列长度
sequences = characters.batch(SEQ_LENGTH+1,drop_remainder=True)
for item in sequences.take(1):
d = tokenizer.index_word
print('dataset索引序列',[i.numpy() for i in item])
print('dataset文本序列',[d[i.numpy()] for i in item])
build_seq_pairs
来帮我们建立成对的句子(输入/输出),这也是为什麽刚刚要 SEQ_LENGTH+1
,因为我们丢入一个长度11的句子,他才能够返回长度各为10的(输入/输出),并使用dataset的内建function map
,让全部的文本都做过一轮def build_seq_pairs(chunk):
input_text = chunk[:-1]
target_text = chunk[1:]
return input_text, target_text
ds = sequences.map(build_seq_pairs)
for idx,item in enumerate(ds.take(2)):
d = tokenizer.index_word
print(f"第 {idx} 个输入句子的索引序列")
print([i for i in item[0].numpy()])
print(f"第 {idx} 个输入句子的文本序列:")
print([d[i] for i in item[0].numpy()])
print()
print(f"第 {idx} 个输出句子的索引序列:")
print([i for i in item[1].numpy()])
print(f"第 {idx} 个输出句子的文本序列:")
print([d[i] for i in item[1].numpy()])
print('-'*100)
BATCH_SIZE
以及设定缓冲区大小 BUFFER_SIZE
重新排列数据集BATCH_SIZE
决定的是模型一次可以看多少个的句子,并使用GPU平行运算(效率)
这也是为什麽我们要使用 tensor
tensorflow
被设定为可以处理无限的序列,它并不会在内存尝试不同的组合,因此需要宣告一个
缓冲区也就是BUFFER_SIZE
,它维持一个缓冲区,在缓冲区重新排列元素
BATCH_SIZE = 64
BUFFER_SIZE = 10000
ds = ds.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
keras
这个来自 tensorflow
的高级API来做实作EMBEDDING_DIM = 512
UNITS = 1024
LEARNING_RATE = 0.001
model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(input_dim=w, output_dim=EMBEDDING_DIM,batch_input_shape=[BATCH_SIZE, None]))
model.add(tf.keras.layers.LSTM(units=RNN_UNITS, return_sequences=True, stateful=True, recurrent_initializer='glorot_uniform'))
model.add(tf.keras.layers.Dense(w))
model.summary()
model.summary()
可以检视已经建完的模型Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (128, None, 512) 2787840
_________________________________________________________________
lstm (LSTM) (128, None, 1024) 6295552
_________________________________________________________________
dense (Dense) (128, None, 5445) 5581125
=================================================================
Total params: 14,664,517
Trainable params: 14,664,517
Non-trainable params: 0
_________________________________________________________________
这里的output指的是文本当中每个字的机率值,然後输出贿选机率最大的那个字
首先检查输出的形状:
for input_example_batch, target_example_batch in ds.take(1):
example_batch_predictions = model(input_example_batch)
print(example_batch_predictions.shape, "# (batch_size, sequence_length, w)")
宣告一个function来作为损失函数(loss_function),本文的问题可以被视为一个有 w
个分类(字)的问题。而要定义分类问题的损失相对简单,使用 sparse_categorical_crossentropy
是个不错的选择
def loss(y_true, y_pred):
return tf.keras.losses.sparse_categorical_crossentropy(y_true, y_pred, from_logits=True)
而 优化器(optimizer) 使用不论在任何任务中,表现都不错的 tf.keras.optimizers.Adam
,除了学习率(learning_rate)
之外,其他采用默认参数,并使用 tf.keras.Model.compile
方法配置训练步骤,以及损失函数
model.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE), loss=loss
)
fit
来训练,并且把资料 ds
、epochs
,作为fit的参数EPOCHS = 30
history = model.fit(
ds,
epochs=EPOCHS,
)
# 储存训练完成的模型
model.save('/content/gdrive/My Drive/LAB/IT_post/model_01.h5')
Epoch 1/10
2668/2668 [==============================] - 694s 260ms/step - loss: 3.3912
Epoch 2/10
2668/2668 [==============================] - 695s 260ms/step - loss: 2.5633
Epoch 3/10
2668/2668 [==============================] - 699s 262ms/step - loss: 2.3310
Epoch 4/10
2668/2668 [==============================] - 696s 261ms/step - loss: 2.2017
Epoch 5/10
2668/2668 [==============================] - 693s 260ms/step - loss: 2.1163
Epoch 6/10
2668/2668 [==============================] - 690s 259ms/step - loss: 2.0567
Epoch 7/10
2668/2668 [==============================] - 689s 258ms/step - loss: 2.0134
Epoch 8/10
2668/2668 [==============================] - 690s 259ms/step - loss: 1.9830
Epoch 9/10
2668/2668 [==============================] - 692s 259ms/step - loss: 1.9613
Epoch 10/10
73/2668 [..............................] - ETA: 11:07 - loss: 2.3482
EMBEDDING_DIM = 512
RNN_UNITS = 1024
BATCH_SIZE = 1
# 定义生成的模型
infer_model = tf.keras.Sequential()
infer_model.add(tf.keras.layers.Embedding(input_dim=w, output_dim=EMBEDDING_DIM, batch_input_shape=[BATCH_SIZE, None]))
infer_model.add(tf.keras.layers.LSTM(units=RNN_UNITS, return_sequences=True, stateful=True))
infer_model.add(tf.keras.layers.Dense(w))
# 载入已储存模型之权重
infer_model.load_weights('/content/gdrive/My Drive/LAB/IT_post/model_01.h5')
infer_model.build(tf.TensorShape([1, None]))
:::success
batch_size会尽量设成2的n次方,因为电脑的记忆体配置是用二进制去做运算
此举会稍微加速(可以实验看看)
:::
load_weights
作为读取权重build
会重建模型架构,而 tf.TensorShape
会将BATCH_SIZE
(维度)固定为1:::info
因为循环神经网路传递状态的方式,一旦建好模型,BATCH_SIZE
就不能做变动了。但在实际生成文章时,我们需要让 BATCH_SIZE
等於 1
:::
infer_model.summary()
text_generated = '梦见老鼠'
# 代表「乔」的索引
a = 1
for i in range(100):
dream = tokenizer.texts_to_sequences([text_generated])[0]
# 增加 batch 维度丢入模型取得预测结果後
# 再度降维,拿掉 batch 维度
input = tf.expand_dims(dream, axis=0)
predictions = infer_model(input)
predictions = tf.squeeze(predictions, 0)
temperature = 1.0
# 利用生成温度影响抽样结果
predictions /= temperature
# 从 4330 个分类值中做抽样
# 取得这个时间点模型生成的中文字
predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()
input_eval = tf.expand_dims([predicted_id], 0)
partial_texts = [ tokenizer.index_word[predicted_id] ]
text_generated += partial_texts[0]
# 成功生成 解梦文字档 → text_generated
# 透过撷取重点解梦文字作呈现
print(text_generated.split('\n')[0].split('。')[0])
自由团队 官方网站:https://aifreeblog.herokuapp.com/
自由团队 Github:https://github.com/AI-FREE-Team/
自由团队 粉丝专页:https://www.facebook.com/AI.Free.Team/
自由团队 IG:https://www.instagram.com/aifreeteam/
自由团队 Youtube:https://www.youtube.com/channel/UCjw6Kuw3kwM_il39NTBJVTg/
文章同步发布於:自由团队部落格
(想看更多文章?学习更多AI知识?敬请锁定自由团队的频道!)
<<: DAY29-如何与人协同工作与好好沟通-英文很重要,中文也很重要,你有注意过你的欧化中文吗?
>>: [Day - 29] React Hooks useEffect 学习笔记
大家好! 以下分享RPA领域龙头UiPath在端午节过後那周的线上大会 活动名称:UiPath De...
疫情时代,视讯串流当头,用视讯镜头来做个线上摄影吧! 自从疫情爆发後,各行各业也开始进行居家办公,...
结构体介绍 CAN_FilterTypeDef typedef struct { uint32_t ...
误差(Error)来自什麽地方? 来自於偏差(Bias) 来自於方差(Variance) 假设我们需...
我们先来看一段闭包程序码 function arrFunction() { const arr = ...