提取caret中nnet的finalModel参数:无法复现模型结果
问题:用caret训练nnet模型后,无法用nnet包单独复现结果?
嘿,我最近在折腾caret和nnet的时候遇到个头疼的问题:当我把caret训练出来的nnet模型参数原封不动地拿到nnet::nnet里调用时,两次的预测结果居然不一样!我明明觉得参数都对齐了啊😤。虽然知道可以直接用caret的模型做预测,但就是想搞明白怎么用nnet单独复现出来,不然总觉得没搞透。
先给大家看一下我用的示例代码:
library(nnet) library(caret) len <- 100 set.seed(4321) X <- data.frame(x1 = rnorm(len, 40, 25), x2 = rnorm(len, 70, 4), x3 = rnorm(len, 1.6, 0.3)) y <- 20000 + X$x1 * 3 - X$x1*X$x2 * 4 - (X$x3**4) * 7 + rnorm(len, 0, 4) XY <- cbind(X, y) # 预处理 preProcPrms <- preProcess(XY, method = c("center", "scale")) XY_pre <- predict(preProcPrms, XY) # 用caret训练nnet controlList <- trainControl(method = "cv", number = 5) tuneMatrix <- expand.grid(size = c(1, 2), decay = c(0, 0.1)) caret_net <- train( x = XY_pre[ , colnames(XY_pre) != "y"], y = XY_pre[ , colnames(XY_pre) == "y"], method = "nnet", linout = TRUE, TRACE = FALSE, maxit = 100, tuneGrid = tuneMatrix, trControl = controlList ) # 尝试用nnet复现 nnet_net <- nnet( x = XY_pre[ , colnames(XY_pre) != "y"], y = XY_pre[ , colnames(XY_pre) == "y"], linout = caret_net$finalModel$param$linout, TRACE = caret_net$finalModel$param$TRACE, size = caret_net$bestTune$size, decay = caret_net$bestTune$decay, entropy = caret_net$finalModel$entropy, maxit = 100 ) # 对比结果 print(caret_net$finalModel) print(nnet_net) y_caret <- predict(caret_net$finalModel, XY_pre[ , colnames(XY_pre) != "y"]) y_nnet <- predict(nnet_net, XY_pre[ , colnames(XY_pre) != "y"]) plot(y_caret, y_nnet, main = "Hard to spot, but y_caret <> y_nnet - which prm have I forgotten?") hist(y_caret - y_nnet)
问题出在哪?
其实你漏了两个关键细节:
- 随机种子:caret的
train函数在交叉验证过程中会自动设置一系列随机种子(存放在caret_net$control$seeds里),最终训练最优模型时用的是列表里最后一个种子。而你直接调用nnet时,用的是全局的种子,和caret内部的种子不一致,导致神经网络的初始权重不一样,结果自然有差异。 rang参数:caret在调用nnet时,默认会把权重的初始范围rang设为0.1,但nnet包的默认值是0.5!这个参数直接影响权重初始化,也是结果差异的重要原因。
怎么修正才能复现结果?
只需要同步这两个点就行,修正后的nnet调用代码如下:
# 同步caret训练最终模型时使用的随机种子 set.seed(caret_net$control$seeds[[length(caret_net$control$seeds)]]) # 调用nnet时加上rang参数,确保和caret默认一致 nnet_net <- nnet( x = XY_pre[, colnames(XY_pre) != "y"], y = XY_pre[, colnames(XY_pre) == "y"], linout = caret_net$finalModel$param$linout, TRACE = caret_net$finalModel$param$TRACE, size = caret_net$bestTune$size, decay = caret_net$bestTune$decay, entropy = caret_net$finalModel$entropy, maxit = 100, rang = 0.1 # 这个是caret调用nnet的默认值,必须加上! )
运行这段代码后,再对比y_caret和y_nnet,你会发现两者几乎完全一致(可能存在极小的浮点精度差异,但可以忽略)。
内容的提问来源于stack exchange,提问作者r.user.05apr
相关产品推荐
相关产品推荐

