简介
R 既可以就地修改,即在内存中修改对象,也可以在修改对象时创建对象的新副本。这是解释here。如果您创建一个对象,以及另一个指向该第一个对象的对象,它们将指向内存中的同一位置。但是,这意味着修改一个会修改另一个。
为避免这种情况,R 会跟踪是否有 1 或 > 1 个名称指向内存中的同一位置。如果为 1,那么修改内存值是安全的。这是就地修改。但是,如果它 > 1,则修改一个对象将修改另一个对象。因此,被修改的对象实际上被复制到了新的内存部分,这样两个对象就不再指向同一块内存了。这意味着修改一个对象不会影响另一个对象。
这不是在 R 的 Keras 中执行的,据我了解(尽管我还没有在 python 中使用 Keras)它也没有在 python 中执行。 Keras 总是使用就地修改,无论有多少名称指向内存中的位置。因此,对一个模型所做的任何事情也会对另一个模型进行,因为实际上它们只是同一个模型的两个名称——两个“对象”实际上只是一个对象。
错误代码示例
为了说明这会在哪里绊倒您,下面是一个通过比较两个 RMSProp 学习率来训练 mnist 分类网络的示例。如果不了解 Keras 中的就地修改,可能会编写代码:
library(keras)
# data
mnist = dataset_mnist()
x_train = mnist$train$x
y_train = mnist$train$y
x_train = array_reshape(x_train, c(nrow(x_train), 784))
x_train = x_train / 255
y_train = to_categorical(y_train, 10)
# model
model = keras_model_sequential()
model %>%
layer_dense(units = 256, activation = 'relu', input_shape = c(784)) %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = 'relu') %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = 'softmax')
# compile and train a model, given a learning rate
comp_train = function(learning_rate) {
model_copy = model
model_copy %>% compile(
loss = 'categorical_crossentropy',
optimizer = optimizer_rmsprop(
lr = learning_rate
),
metrics = c('accuracy')
)
training_history = model_copy %>% fit(
x_train, y_train,
epochs = 30, batch_size = 128,
validation_split = 0.2
)
return(
as.data.frame(training_history)
)
}
# test two learning rates
lr_0.001 = comp_train(0.001)
lr_0.0001 = comp_train(0.0001)
结果对于 0.001 的学习率是有意义的:
但是,对于 0.0001 的学习率,结果非常出乎意料:
如果人们意识到第二张图像只是第一张图像的 30 个 epoch 的延续,那么这些结果并不出人意料。所以,放在一起,这两张图片只是显示了同一个神经网络在 60 个 epoch 上的训练。这是因为就地修改——当你训练“第二个”网络时,你实际上只是在训练第一个,即使它已经被训练过。
示例工作代码
那么应该做些什么不同的事情呢?对于 Keras,不同的模型必须分别使用 keras_model_sequential() 或 keras_model()(无论您使用哪种类型)进行初始化。所以我们分别定义每个模型:
library(keras)
# data
mnist = dataset_mnist()
x_train = mnist$train$x
y_train = mnist$train$y
x_train = array_reshape(x_train, c(nrow(x_train), 784))
x_train = x_train / 255
y_train = to_categorical(y_train, 10)
# models
model_lr0.001 = keras_model_sequential()
model_lr0.0001 = keras_model_sequential()
model_lr0.001 %>%
layer_dense(units = 256, activation = 'relu', input_shape = c(784)) %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = 'relu') %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = 'softmax')
model_lr0.0001 %>%
layer_dense(units = 256, activation = 'relu', input_shape = c(784)) %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = 'relu') %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = 'softmax')
# compile and train a given model, also given a learning rate
comp_train = function(model, learning_rate) {
model %>% compile(
loss = 'categorical_crossentropy',
optimizer = optimizer_rmsprop(
lr = learning_rate
),
metrics = c('accuracy')
)
training_history = model %>% fit(
x_train, y_train,
epochs = 30, batch_size = 128,
validation_split = 0.2
)
return(
as.data.frame(training_history)
)
}
# test two learning rates
lr_0.001 = comp_train(model_lr0.001, 0.001)
lr_0.0001 = comp_train(model_lr0.0001, 0.0001)
这一次,我们得到了预期的结果:
我们现在可以成功比较两个学习率。 “更好”的工作代码是在函数中定义模型(使用keras_model_sequential()),这也给出了预期的结果。这留给读者作为练习。