使用LSTM生成文本Deep learning with R 第八章第一节
使用前面的语言来预测下一个或多个标记。例如: the cat is on the ma, 用训练网络来预测目标t。
首先训练好一个语言模型(能够对下一个标记的概率进行建模的网络叫做语言模型),能够捕捉到语言的潜在空间。在UN练好了模型之后从中采样(sample,生成新序列)。
下面将会用到一个LSTM层,训练N个字符,来预测N+1个字符。这个LSTM叫做字符级的神经网络语言模型。
采样策略
采样策略有两种方式:贪婪采样和随机采样。
贪婪采样
始终选择可能性最大的下一个字符(某个字符的概率为1,其他字符的概率为0)
随机采样
如果下一个字符字符e的概率为30%,那么会有30%的概率选择它。
softmax温度
为了控制随机性的大小,引入了softmax温度。
reweight_distribution <- function(original_distribution,
temperature = 0.5) {
distribution <- log(original_distribution) / temperature
distribution <- exp(distribution)
distribution / sum(distribution)
}
温度预告代表熵更大,随机性越大,越不确定。
实现LSTM文本生成
下载文件
library(keras)
library(stringr)
path <- get_file(
"nietzsche.txt",
origin = "https://s3.amazonaws.com/text-datasets/nietzsche.txt"
)
text <- tolower(readChar(path, file.info(path)$size)) #大小写统一
cat("Corpus length:", nchar(text), "\n") #文本库长度
数据处理
接下来,提取maxlen长度的序列(序列之间有重叠),进行one-hot编码,之后进行reshape(sequences,maxlen,unique_characters)。准备数组y,即我们的目标。
maxlen <- 60 #60个字符一个新序列
step <- 3 #每3个字符采样一个新序列
text_indexes <- seq(1, nchar(text) - maxlen, by = step) #生成采样序列
sentences <- str_sub(text, text_indexes, text_indexes + maxlen - 1) #提取句子的文本
next_chars <- str_sub(text, text_indexes + maxlen, text_indexes + maxlen) #提取目标的文本
cat("Number of sequences: ", length(sentences), "\n")
chars <- unique(sort(strsplit(text, "")[[1]])) #取出text所有字母和标点(不重复),好像还有中文。(为y做准备)
cat("Unique characters:", length(chars), "\n")
char_indices <- 1:length(chars)
names(char_indices) <- chars #每个字符打上序号
cat("Vectorization...\n")
x <- array(0L, dim = c(length(sentences), maxlen, length(chars))) #数据整形(句子数量,序列长度,所有字符的数量)
y <- array(0L, dim = c(length(sentences), length(chars))) # y整形(目标数量,所有字符数量)
for (i in 1:length(sentences)) { #x,y二进制编码
sentence <- strsplit(sentences[[i]], "")[[1]]
for (t in 1:length(sentence)) {
char <- sentence[[t]]
x[i, t, char_indices[[char]]] <- 1 #x整形
}
next_char <- next_chars[[i]]
y[i, char_indices[[next_char]]] <- 1 #y整形
}
构建网络
构建网络:LSTM层加一个Dense(softmax)分类器。1Dconv也可以生成。
model <- keras_model_sequential() %>%
layer_lstm(units = 128, input_shape = c(maxlen, length(chars))) %>%
layer_dense(units = length(chars), activation = "softmax")
optimizer <- optimizer_rmsprop(lr = 0.01)
model %>% compile(
loss = "categorical_crossentropy",
optimizer = optimizer
)
训练模型并且采样
给定一个训练好的模型和一个种子文本片段,重复操作一下操作生成新文本
1.给定文本,从模型中的刀下一个字符的概率分布。
2.根据文具重新加权
3.根据重新加权后的分布对下一个字符进行随机采样
4.将新的字符加到文本末尾
给定模型预测,采样下一个字符的函数
sample_next_char <- function(preds, temperature = 1.0) {
preds <- as.numeric(preds)
preds <- log(preds) / temperature
exp_preds <- exp(preds)
preds <- exp_preds / sum(exp_preds)
which.max(t(rmultinom(1, 1, preds)))
}
下面这个循环将反腐训练并生成文本。在每轮过后将使用一系列不同的温度值来生成文本。
for (epoch in 1:60) { #循环60次
cat("epoch", epoch, "\n") #循环次数实时查看
model %>% fit(x, y, batch_size = 128, epochs = 1) #train
start_index <- sample(1:(nchar(text) - maxlen - 1), 1) #输入文本的开头序号
seed_text <- str_sub(text, start_index, start_index + maxlen - 1) #输入文本
cat("--- Generating with seed:", seed_text, "\n\n")
for (temperature in c(0.2, 0.5, 1.0, 1.2)) { #温度变化选项
cat("------ temperature:", temperature, "\n")
cat(seed_text, "\n")
generated_text <- seed_text
for (i in 1:400) { #从文本生成生成400个字符
sampled <- array(0, dim = c(1, maxlen, length(chars))) #目前生成的数据进行onehot 编码
generated_chars <- strsplit(generated_text, "")[[1]]
for (t in 1:length(generated_chars)) {
char <- generated_chars[[t]]
sampled[1, t, char_indices[[char]]] <- 1
}
preds <- model %>% predict(sampled, verbose = 0) #下一个字符采样
next_index <- sample_next_char(preds[1,], temperature)
next_char <- chars[[next_index]]
generated_text <- paste0(generated_text, next_char)
generated_text <- substring(generated_text, 2)
cat(next_char)
}
cat("\n\n")
}
}
训练一轮要很久啊,就不放了