使用前面的语言来预测下一个或多个标记。例如: 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")
  }
}

训练一轮要很久啊,就不放了

Logo

更多推荐