你的机器“不肯”学习,怎么办?

给你讲讲机器学习数据预处理中,归一化(normalization)的重要性。

前情回顾

Previously, on 玉树芝兰 ……

我给你写了一篇《如何用 Python 和 Tensorflow 2.0 神经网络分类表格数据?》,为你讲解了 Tensorflow 2.0 处理结构化数据的分类。

结尾处,我给你留了一个问题

把测试集输入模型中,检验效果。结果是这样的:

model.evaluate(test_ds)

准确率接近80%,看起来很棒,对吗?

但是,有一个疑问:

注意这张截图。训练的过程中,除了第一个轮次外,其余4个轮次的这几项重要指标居然都没变

它们包括:

  • 训练集损失
  • 训练集准确率
  • 验证集损失
  • 验证集准确率

所谓机器学习,就是不断迭代改进。

如果每一轮下来,结果都一模一样,这里八成有鬼。

我给了你提示:

看一个分类模型的好坏,不能只看准确率(accuracy)。对于二元分类问题,你可以关注一下 f1 score,以及混淆矩阵(confusion matrix)。

这段时间,你通过思考,发现问题产生原因,以及解决方案了吗?

从留言的反馈来看,有读者能够正确指出了问题。

但很遗憾,我没有能见到有人提出正确和完整的解决方案。

这篇文章,咱们就来谈谈,机器为什么“不肯学习”?以及怎么做,才能让它“学得进去”。

环境

本文的配套源代码,我放在了这个 Github 项目中。请你点击这个链接http://t.cn/ESJmj4h)访问。

如果你对我的教程满意,欢迎在页面右上方的 Star 上点击一下,帮我加一颗星。谢谢!

注意这个页面的中央,有个按钮,写着“在 Colab 打开”(Open in Colab)。请你点击它。

然后,Google Colab 就会自动开启。

我建议你点一下上图中红色圈出的 “COPY TO DRIVE” 按钮。这样就可以先把它在你自己的 Google Drive 中存好,以便使用和回顾。

Colab 为你提供了全套的运行环境。你只需要依次执行代码,就可以复现本教程的运行结果了。

如果你对 Google Colab 不熟悉,没关系。我这里有一篇教程,专门讲解 Google Colab 的特点与使用方式。

为了你能够更为深入地学习与了解代码,我建议你在 Google Colab 中开启一个全新的 Notebook ,并且根据下文,依次输入代码并运行。在此过程中,充分理解代码的含义。

这种看似笨拙的方式,其实是学习的有效路径

代码

请你在 Colab Notebook 里,找到这一条分割线:

用鼠标点击它,然后从菜单里面选择 Runtime -> Run Before

运行结束后,你会获得如下图的结果:

如何用 Python 和 Tensorflow 2.0 神经网络分类表格数据?》一文的结果已经成功复现。

下面我们依次来解读后面的语句。

首先,我们利用 Keras API 中提供的 predict 函数,来获得测试集上的预测结果。

pred = model.predict(test_ds)

但是请注意,由于我们的模型最后一层,用的激活函数是 sigmoid , 因此 pred 的预测结果,会是从0到1区间内的小数。

而我们实际需要输出的,是整数0或者1,代表客户“流失”(1)或者“未流失”(0)。

幸好, numpy 软件包里面,有一个非常方便的函数 rint ,可以帮助我们四舍五入,把小数变成整数。

pred = np.rint(pred)

我们来看看输出结果:

pred

有了预测输出结果,下面我们就可以用更多的方法,检验分类效果了。

根据前文的提示,这里我们主要用到两项统计功能:

  • 分类报告
  • 混淆矩阵

我们先从 Scikit-learn 软件包导入对应的功能。

from sklearn.metrics import classification_report, confusion_matrix

然后,我们对比测试集实际标记,即 test['Exited'] ,和我们的预测结果。

print(classification_report(test['Exited'], pred))

这里,你立刻就能意识到出问题了——有一个分类,即“客户流失”(1)里,三项重要指标(precision, recall 和 f1-score)居然都是0!

我们用同样的数据查看混淆矩阵,看看到底发生了什么。

print(confusion_matrix(test['Exited'], pred))

混淆矩阵的读法是,行代表实际分类,列代表预测分类,分别从0到1排列。

上图中,矩阵的含义就是:模型预测,所有测试集数据对应的输出都是0;其中预测成功了1585个(实际分类就是0),预测错误415个(实际分类其实是1)。

也就是说,咱们费了半天劲,训练出来的模型只会傻乎乎地,把所有分类结果都设置成0.

在机器学习里,这是一个典型的笨模型(dummy model)。

如果咱们的测试集里面,标签分类0和1的个数是均衡的(一样一半),那这种笨模型,应该获得 50% 的准确率。

然而,我们实际看看,测试集里面,分类0(客户未流失)到底占多大比例:

len(test[test['Exited'] == 0])/len(test)

结果是:

0.7925

这个数值,恰恰就是《如何用 Python 和 Tensorflow 2.0 神经网络分类表格数据?》一文里面,我们在测试集上获得了准确率。

一开始我们还认为,将近80%的准确率,是好事儿。

实际上,这模型着实很傻,只有一根筋。

设想我们拿另外一个测试集,里面只有 1% 的标注是类别0,那么测试准确率也就只有 1% 。

为了不冤枉模型,咱们再次确认一下。

使用 numpy 中的 unique 函数,查看一下预测结果 pred 中,到底有几种不同的取值。

np.unique(pred)

结果是:

array([0.], dtype=float32)

果不其然,全都是0.

果真是“人工不智能”啊!

分析

问题出在哪里呢?

模型根本就没有学到东西。

每一轮下来,结果都一样,毫无进步。

说到这里,你可能会有疑惑:

老师,是不是你讲解出错了?

两周前,我在 UNT 给学生上课的时候,他们就提出来了这疑问。

我早有准备,立即布置了一个课堂练习。

让他们用这套流程,处理另外的一个数据集。

这个数据集你也见过,就是我在《贷还是不贷:如何用Python和机器学习帮你决策?》里面用过的贷款审批数据。

我把数据放在了这个链接http://t.cn/ESJ3x3o),你如果感兴趣的话,不妨也试着用前文介绍的流程,自己跑一遍。

学生们有些无奈地做了这个练习。

他们的心理活动大概是这样的:

你教的这套流程,连演示数据都搞不定,更别说练习数据了。做了也是错的。是不是先纠正了错误,再让我们练啊?

然后,当运行结果出来的时候,我在一旁,静静看着他们惊诧、沉思,以至于抓狂的表情。

同一套流程,在另外的数据上使用,机器确实学习到了规律

数据集的细节里面,藏着什么魔鬼?

归一

直接说答案:

流程上确实有问题。数值型数据没有做归一化(normalization)。

归一化是什么?

就是让不同特征列上的数值,拥有类似的分布区间。

最简单的方法,是根据训练集上的对应特征,求 Z 分数。

Z 分数的定义是:

  • Mean 是均值
  • Standard Deviation 是标准差

为什么一定要做这一步?

回顾一下咱们的数据。

我这里用红色标出来了所有数值特征列。

看看有什么特点?

对,它们的分布迥异。

NumOfProducts 的波动范围,比起 Balance 或者 EstimatedSalary,要小得多。

机器学习,并不是什么黑科技。

它的背后,是非常简单的数学原理。

最常用的迭代方法,是梯度下降(Gradient descent)。如下图所示:

其实就是奔跑着下降,找局部最优解。

如果没跑到,继续跑。

如果跑过辙了,再跑回来。

但问题在于,你看到的这张图,是只有1维自变量的情况。

咱们观察的数据集,仅数值型数据,就有6个。因此至少是要考察这6个维度。

不好意思,我无法给你绘制一个六维图形,自己脑补吧。

但是注意,对这六个维度,咱们用的,却是同一个学习速率(learning rate)。

就好像同一个老师,同时给6个学生上数学课。

如果这六个维度分布一致,这样是有意义的。

这也是为什么大多数学校里面,都要分年级授课。要保证授课对象的理解能力,尽量相似。

但假如这“6个学生”里,有一个是爱因斯坦,一个是阿甘。

你说老师该怎么讲课?

爱因斯坦听得舒服的进度,阿甘早就跟不上了。

阿甘能接受的进度,爱因斯坦听了可能无聊到想撞墙。

最后老师决定——太难了,我不教了!

于是谁都学不到东西了。

对应到我们的例子,就是因为数据分布差异过大,导致不论往哪个方向尝试改变参数,都按下葫芦浮起瓢,越来越糟。

于是模型判定,呆在原地不动,是最好的策略

所以,它干脆不学了。

怎么办?

这个时候,就需要归一化了。

对应咱们这个不恰当的举例,就是在课堂上,老师要求每个人都保持每天一单位(unit)的学习进度。

只不过,爱因斯坦的一单位,是100页书。

阿甘同学……两行,还能接受吧?

新代码

请你点击这个链接http://t.cn/ESJBJHW)访问更新后的代码。

按照之前的方式,点击“在 Colab 打开”(Open in Colab)。使用 “COPY TO DRIVE” 按钮,存放在你自己的 Google Drive 中。

对比观察后,你会发现,改动只有1个代码段落

就是把原先的数值型特征采集从这样:

for header in numeric_columns:
  feature_columns.append(feature_column.numeric_column(header))

变成这样:

for header in numeric_columns:
  feature_columns.append(
      feature_column.numeric_column(
          header,
          normalizer_fn=lambda x: (tf.cast(x, dtype=float)-train[header].mean())/train[header].std()))

尤其要注意,我们要保证平均值和标准差来自于训练集。只有这样,才能保证模型对验证集和测试集的分布一无所知,结果的检验才有意义。否则,就如同考试作弊一样。

这就是为了归一化,你所需做的全部工作。

这里我们依然保持原先的随机种子设定。也就是凡是使用了随机函数的功能(训练集、验证集和测试集的划分等),都与更新代码之前完全一致

这样做,改变代码前后的结果才有可对比性

下面我们使用菜单栏里面的 "Run All" 运行一下代码。

之后查看输出。

首先我们可以注意到,这次的训练过程,数值终于有变化了。

因为其他变量全都保持一致。所以这种变化,没有别的解释,只能是因为使用了归一化(normalization)。

我们更加关心的,是这次的分类报告,以及混淆矩阵。

分类报告是这样的:

注意这一次,类别1上面的几项指标,终于不再是0了。

混淆矩阵中,类别1里,也有36个预测正确的样本了。

成功了!

……

别急着欢呼。

虽然机器在学习和改进,但是效果好像也不是很好嘛。例如类别1的 Recall 简直惨不忍睹。

有没有什么办法改进呢?

这个问题,就需要你了解如何微调模型,以及超参数的设定了。

我把推荐的学习资料,放在了公众号的对应文章里,欢迎查看。

小结

这篇文章里,我为你介绍了以下知识点:

  • 分类模型性能验证(尤其是 Accuracy 之外的)评测指标;
  • 预处理过程中数值数据归一化(Normalization)的重要性;
  • 如何在 Tensorflow 2.0 的数据预处理和特征抽取中使用归一化;
  • 如何利用模型预测分类结果,并且使用第三方软件包功能快速统计汇报。

希望上述内容,能对你使用深度神经网络进行机器学习有帮助。

祝深度学习愉快!

延伸阅读

你可能也会对以下话题感兴趣。点击链接就可以查看。

喜欢请点赞和打赏。还可以微信关注和置顶我的公众号“玉树芝兰”(nkwangshuyi)

如果你对 Python 与数据科学感兴趣,不妨阅读我的系列教程索引贴《如何高效入门数据科学?》,里面还有更多的有趣问题及解法。

题图:来自于 freepixels

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容