说明分类或回归模型所做的降维

数据挖掘 机器学习 分类 支持向量机 随机森林 降维
2021-10-01 22:25:07

Tl;DR:你可以预测一些事情,但你如何解释预测?


您通常的分类/回归设置

假设数据是一个经典的回归/分类问题:几个数值列,几个名义列,以及我们试图预测的事件:

user1,年龄:18,财富:20000,喜欢:西红柿,isInBigCity:是,hasClicked:是
user2,年龄:25,财富:24000,喜欢:carrots,isInBigCity:否,hasClicked:否
...

在随机森林、SVM、逻辑回归、深度神经网络或其他方法的帮助下,我们导出了一个模型,该模型可以hasClicked:yes为面临点击选择的新用户输出事件的概率。


提取模型显示的内部拓扑

现在,这些算法做了一些降维,将这些输入减少到一个概率。我的问题是:你将如何提取这些模型正在做什么并向人类展示降维步骤?您将如何说明与预测类有关的数据集的内部拓扑?

我正在寻找:

  • 由随机森林、SVM、逻辑回归、深度神经网络生成的模型正在做什么的可视化。

  • 从回归/分类模型中提取的聚类器(当然可以将单个决策树视为分层聚类器)

  • 在 T-SNE 的欧几里得距离有意义的空间中投影输入数据的特定于模型的方法。

  • 一种从回归/分类模型中学习 T-SNE 兼容距离的方法。

  • 优化一个变量的分离而不使用它进行聚类的聚类方法。

  • 由回归/分类模型构建的聚类器


目标是根据用户的行为提取某种市场细分。并对其进行高级可视化。可以清楚地揭示某些用户比其他用户转换得更好的原因。

编辑:让我们将问题简化为一个估计器:随机森林;你的答案是什么?

1个回答

(注意:这个答案是中期编辑)
有许多机器学习器解释器和诊断器。

免责声明:(这些应该随着时间的推移而增加)

  • 我并没有让它完全可复制,因为它的长度是原来的 2 倍,而且无论如何它都在努力变得像书一样。
  • 这更多的是展示方法而不是进入疯狂的细节。如果您想深入了解细微差别,那是另一个问题。

让我们设置一个示例问题。

Mnist 是一个公平的数据集,所以让我们首先使用随机森林来描述它,然后对学习器进行活体解剖以了解它是什么、为什么以及如何工作的。

这是我首选的“启动”,因为如果我没有它,它确保我得到它。

#list of packages
listOfPackages <- c('dplyr',      #data munging
                    'ggplot2',    #nice plotting
                    'data.table',  #fast read/write
                    'matrixStats', #column standard deviances
                    'h2o',         #decent ML toolbox
                    'keras')       #has mnist data

#if not installed, then install and import
for (i in 1:length(listOfPackages)){
  if(!listOfPackages[i] %in% installed.packages(fields = "Package")){
    install.packages(listOfPackages[i] , dependencies = TRUE)
  }
  require(package = listOfPackages[i], character.only = T, quietly = T)
}
rm(i, listOfPackages)

这是使用 keras 读取 mnist 的代码:

library(keras)
mnist <- dataset_mnist()
x_train <- mnist$train$x
y_train <- mnist$train$y
x_test <- mnist$test$x
y_test <- mnist$test$y

一个人必须做一些家务:

# Redefine  dimension of train/test inputs
x_train <- array_reshape(x_train, c(nrow(x_train), img_rows*img_cols))
x_test <- array_reshape(x_test, c(nrow(x_test), img_rows*img_cols))

df_train <- data.frame(y=y_train, x_train)
df_test  <- data.frame(y=y_test , x_test)

以下是如何使用 h2o.ai 使用随机森林处理它,假设它已经安装:

# Input image dimensions
img_rows <- 28
img_cols <- 28

#spin up h2o
h2o.init(nthreads = -1)

#move data to h2o
train.hex <- as.h2o(df_train, "train.hex")
test.hex <- as.h2o(df_test, "test.hex")

#prep for random forest
x <- 1:ncol(x_train)
y <- 1
x <- x[-y]

#spin up random forest 
myrf <- h2o.randomForest(x, y,
                         training_frame = train.hex,
                         validation_frame = test.hex,
                         ntrees = 150, model_id = "myrf.hex")

这是它是如何做到的。

混淆矩阵: 在此处输入图像描述
在此处输入图像描述

这是训练/有效指标
在此处输入图像描述
在此处输入图像描述

所以呢?所以现在怎么办?我们有一个不错的模型,它与 ( this ) 基准粗略兼容,该基准表明有些东西的错误比它少。它哪里出错了?

测试数据集中大约有 320 个错误分类,并且超出了对每个错误分类的范围。9、8、2 和 3 看起来最差。让我们看看 8 和 3。

h2o.ai 给出了警告:

Warning message:
In .h2o.processResponseWarnings(res) :
  Dropping bad and constant columns: [X701, X702, X309, X672, X61, X673, X60, X674, X63, X62, X65, X64, X66, X393, X778, X779, X781, X782, X420, X783, X421, X700, X78, X79, X141, X142, X780, X448, X449, X725, X726, X727, X728, X729, X81, X80, X83, X82, X85, X730, X84, X698, X731, X87, X699, X732, X86, X337, X88, X559, X561, X169, X10, X12, X11, X14, X13, X281, X16, X15, X18, X17, X560, X19, X504, X505, X111, X112, X113, X476, X477, X753, X21, X754, X20, X755, X23, X22, X25, X24, X27, X26, X29, X28, X197, X616, X617, X30, X225, X588, X32, X589, X31, X34, X33, X36, X35, X38, X37, X39, X646, X767, X768, X769, X253, X770, X771, X772, X773, X41, X532, X774, X40, X533, X775, X43, X776, X42, X777, X45, X1, X44, X2, X47, X3, X46, X4, X49, X5, X48, X6, X7, X8, X9, X756, X757, X758, X759, X760, X50, X365, X761, X366, X762, X52, X367, X763, X51, X764, X54, X644, X765, X53, X645, X766, X56, X55, X58, X57, X59].

夏洛克·福尔摩斯(大约)说: 一旦你排除了不可能,剩下的无论多么不可能,都必须[包含]真相。

让我们从像素中消除不可能的事情。

bad_cols <- which(colSds(x_train)==0)
str(bad_cols)

784 个像素(列)中有 161 个(大约 21%)没有信息。

让我们看看 RF 认为最重要的地方在哪里。

#pop importance
myimp <- h2o.varimp(myrf)

#sort by relative importance
myimp <- myimp %>% arrange(-relative_importance)
myimp$index <- 1:nrow(myimp)

#plot importances
ggplot(myimp,aes(x=log10(index), y=log10(scaled_importance))) +
  geom_point()

产生这个作为一个情节:

在此处输入图像描述

这告诉我们,不足为奇的是,大部分重要性仅在于几个像素强度值。我喜欢想到帕累托、80/20 法则、价格定律或洛特卡定律。这也表明,周围102.5介入,关系崩溃,不再表现出与单一 Lotka 风格规则一致的行为。可能存在“‘物理学’的转变”,但更有可能是数据停止存在的“悬崖”。

接下来我想看看“国王”。特别是,我想知道要获得相同的元素需要多少元素。我可以暴力破解它,但我不想。当我查看情节时,我看到有跳跃的集群,我使用这些来确定要考虑的测试大小。我看到索引值为 10 和 19 的转换。

让我们进行子集化,然后从 LIME 开始。

(注意:这个答案是中期编辑)