2019年12月2日星期一

Git_014:最佳敏捷分支策略

目前业界有三种分支策略:Git Flow,Github Flow,Gitlab Flow,它们都是采用"功能驱动式开发"(Feature-driven development,FDD)。所谓 FDD,指的是需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(bugfix 或 hotfix branch)。完成开发后,该分支就合并到主分支,然后该分支被删除。

在分析了三种分支策略后,笔者发现三种策略都有某些缺陷,不能完全满足敏捷开发、持续集成、持续部署的要求。

为此,笔者提出最佳敏捷分支策略。


1. 主分支
代码库有两个主分支作为常设分支。

  • master
  • release

1.1 master 分支
master 分支用于日常开发,存放最新的代码。
master 分支对应测试环境,一套代码版本至少需要搭建两套测试环境,分别用于系统测试(由测试人员测试)和验收测试(由业务人员测试)。
如何保护 master 分支❓

1.2 release 分支
release 分支用于跟踪线上版本,存放所有对外正式发行的版本。
release 分支对应生产环境。
所有测试通过后,从 master 分支创建 release 分支,格式型如 release/1.0,同时在该 release 分支上打 Tag,格式型如: 1.0,注意前面不要加 v,v1.0 不符合习惯

如何保护 release 分支❓

2. 临时分支
  • feature
  • bugfix
  • hotfix
2.1  feature 分支
feature 分支是功能分支,用于开发各个功能模块,格式型如 feature/request-number
开发人员领取各个功能任务后,从 master 分支创建各自的 feature 分支,通过单元测试和集成测试(由开发人员测试)后,最终合并到 master 分支
所有 feature 分支都合并到 master 分支后,创建系统测试环境,由测试人员进行系统测试;创建验收测试环境,由业务人员进行验收测试。

2.2. bugfix 分支
bugfix 分支是补丁分支,用于修复在测试环境中测试出来的各个 bug,格式型如 bugfix/bug-number
在测试环境中测试出来的 bug,由开发人员领取任务,从 master 分支创建各自的 bugfix 分支进行修复,开发人员需要为每个 bugfix 单独编写单元测试和集成测试,测试通过后,最终合并到 master 分支

2.3. hotfix 分支
hotfix 分支是热补丁分支,用于修复在生产环境中发现的各个 bug,格式型如 hotfix/bug-number
hotfix 分支对应准生产环境,一套代码版本至少需要搭建两套测试环境,分别用于系统测试(由测试人员测试)和验收测试(由业务人员测试)。
在生产环境中发现的 bug,由开发人员领取任务,从当前发现问题的 release 分支创建各自的 hotfix 分支进行修复,开发人员需要为每个 hotfix 单独编写单元测试和集成测试,
所有测试通过后,将 hotfix 分支合并到 master 分支,然后创建 release 分支,格式型如 release/1.0.x,同时在该 release 分支上打 Tag,格式型如: 1.0.x,注意前面不要加 v,v1.0 不符合习惯。

参考文献:
1. https://gist.github.com/digitaljhelms/4287848
2. http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
3. https://www.cnblogs.com/doit8791/p/10147321.html
4. https://blog.csdn.net/hu_zhiting/article/details/95030586
5. http://www.ruanyifeng.com/blog/2012/07/git.html
6. https://www.atlassian.com/blog/git/simple-git-workflow-is-simple
7. https://mohamedradwan.com/2018/01/08/promoting-your-application-deployment-to-different-environments-from-branches-with-and-without-feature-toggle/

2019年2月14日星期四

Scrum_004:Scrum 的三个文档

在 Scrum 中有三个文档是必须要写的,它们是:product backlog、sprint backlog、burn-down chart。

1. product backlog
product backlog 是整个项目的需求概要文档。由 PO 专职负责,一般每周更新一次,在项目的整个生命周期间存续。

用一句简短的、用户熟悉的语言表达的需求,是用户讲给开发人员的故事,不是开发人员讲给用户的故事。
用户故事一般用三段论式的方式描述:
As a XX, I want to XX, so that XX.
作为一个XX角色, 我想要XX功能, 以便于实现 XX 商业价值的目的

例如:作为一个家庭主妇,我需要一个 30 平米的餐厅,以便于我招待 10 位朋友用餐。
例如:作为系统的合法用户,可以通过录入账号和密码登录到系统中。

PO 根据对客户、对最终用户的商业价值来划分用户故事优先级。
PO 通过和客户进一步调研,以及和团队成员沟通,确定和完善用户故事的验收标准。

用户故事 “3C” 原则

  1. Card 可以写在卡片上
  2. Conversation 用于对话
  3. Confirmation 及时确认

至此,product backlog => 需求 + 优先级 => 用户故事 + 优先级 + 验收标准 => 用户角色 + 功能 + 目的 + 优先级 + 验收标准
前面说的都是功能需求,对于非功能需求怎么办?
如果能够明确到某个故事,就描述在故事的验收标准中;如果不能明确到某个故事,就单独写个“技术故事”,单独列出来这些非功能需求。

理想的用户故事具有“ INVEST”特点:
(1)独立性 Independent
尽可能避免故事之间存在依赖关系,否则会造成划分优先级的困难,在安排开发顺序时需要考虑故事之间的依赖关系。
(2)可协商性 Negotiable
故事是可协商的,故事是有弹性的,不是必须实现的书面合同或者需求。
(3)对客户有价值 Valuable
确保每个故事对客户有价值的最好方式是让用户编写故事。
(4)可预测性 Estimable
开发者应该能够预测故事的规模,以及编码实现所需要的时间。
(5)短小精悍 Small
一般一个故事在一个迭代周期内是可以实现的。
(6)可测试性 Testable
所编写的故事必须是可测试的,能够定义出验收标准。

用户故事是如何估算的?
用户故事的估算单位是故事点(Story Points),代表了开发用户故事所需的全部工作量,所以团队的估算必须考虑到影响工作量的所有因素。这可能包括:
  • 工作的数量
  • 工作的复杂度
  • 工作中存在的任何风险或不确定性
故事点是一个相对值,一般用 Fibonacci 数列来表示:0, 1/2, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89。虽然可以分为 12 个等级,但一般只采用0、1、2、3、5、8、13 这 7 个等级。如果在预估中发现超过 13 的,一般把任务分割为两部分。

等待 PO 基于商业价值挑选完毕某次交付中应该包含的用户故事后,接下来,Team 就可以挑选在某次迭代中要实现的用户故事了。

2. sprint backlog
sprint backlog 是在 sprint planning meeting 上讨论确认的任务列表。每个任务以小时为单位,一般没有超过 16 小时的任务。
sprint backlog 由 Team 专职负责,每天更新一次,在 spring 生命周期间存续。

每个任务完成的标志是什么?
(1)Team 成员检测:所有的单元测试通过。
(2)PO 检测:所有的功能测试通过。

3. burn down chart
burn down chart 译为燃尽图,还是很形象的,显示当前 spint 中未完成的任务数目,是展示项目进展的一个指示器。
每天开完 15 分钟站立会议后,由 SM 负责更新燃尽图。


横坐标为工作日期,纵坐标估计剩余的工作量,每个点代表了在那一天估计剩余的工作量。通过折线依次连接起所有的点形成为估计剩余工作量的趋势线。另外还有一条控制线,为最初的估计工作量到结束日期的连线,一般用不同的颜色画上边的两根线。
对此图的研判规则如下:
(1)如果趋势线在控制线以下,说明进展顺利,有比较大的概率按期或提前完工。
(2)如果趋势线在控制线以上,说明有比较大的概率延期,此时需要关注进度了。

注意,趋势线并非一直下行,也有可能上行,即发生了错误的估计或遗漏的任务时。

Scrum_003:Scrum 的五个会议

1. sprint plan meeting

WHY - 为本次 sprint 制定计划
WHO - 全体成员
WHEN - sprint 第 1 天
WHAT - 本次 sprint 要交付的内容
  • PO 讲解 product backlog 
  • 团队进行评估,得出 sprint backlog 
  • 评估每个任务的工作量
  • Team 成员领取任务
HOW LONG - 8 小时
INPUT - 产品列表,最新增量,团队容量,历史数据
OUTPUT - sprint backlog

2. daily scrum meeting

WHY - 制定 24 小时的计划
WHO - Scrum Master 和 Team
WHEN - 每天在固定的时间、规定地点开站立会议
WHAT - 每个 Team 成员依次讲三个问题:
  • 我昨天作了什么?
  • 我今天要做什么?
  • 我有什么问题?
HOW LONG - 15 分钟
INPUT - sprint backlog
OUTPUT - sprint backlog

3. product backlog refinement meeting

WHY - 团队成员帮助 PO 完善下一个迭代的用户故事
WHO - 全体成员
WHEN - 本次 sprint 进行中
WHAT -
  • 解释用户故事,排定优先级
  • 分解用户故事,评估工作量
  • 更新验收标准
HOW LONG - 2 小时
INPUT - product backlog
OUTPUT - product backlog

4. sprint review meeting

WHY - 检视增量,并做调整
WHO - 全体成员
WHEN - 本次 sprint 结束时
WHAT - Team 团队向 PO 和利益相关者展示已完成的功能
  • 检查哪些做了,哪些没做
  • 问题总结
  • 成果演示
  • 下一步计划
HOW LONG - 4 小时
INPUT - 增量、product backlog、问题列表
OUTPUT - product backlog、sprint backlog

5. sprint retrospective meeting
WHY - 为下一个 sprint 作改进
WHO - 全体成员
WHEN - sprint review meeting 结束之后
WHAT - 回顾与总结,提高团队效率,避免重复犯错
  • 检视
  • 调整
  • 计划
HOW LONG - 1 小时
INPUT - Burn Down Chart、评审会议结果、调查结果、sprint backlog
OUTPUT - 改进计划

Scrum_002:Scrum 的三大角色

Scrum 的三大角色是:Scrum Master、Product Owner、Team。这三个对等地位的角色构成一个平衡的铁三角推动整个项目的进展。
注意,这是三个角色,不是某三个人,每个角色可以有一个或多个人担任。
一个 Scrum 团队一般不超过 10 个人,这样才能保证团队的效率比较高。

1. Scrum Master
SM 在项目组承担了如下的细分角色:
(1)会议主持
负责主持四个主要的会议:迭代计划会议、每日站立会议、迭代评审会议、迭代回顾会议。
(2)牧羊犬
负责屏蔽项目组外部的干扰。
(3)雷锋
给 PO、Team 提供帮助:帮助 PO 确定需求、排定优先级;帮助 Team 做估算、分解任务、完成任务。
(4)外交官
当项目组外有人不理解项目组的工作时,负责去解释说明,负责对外发布项目组的信息。
(5)教练
提供 Scrum 培训,负责指导项目组成员按照 Scrum 的原则、方法做事。
(6)清道夫
负责排除在项目进展中遇到的各种障碍。

注意:SM 不是项目经理,他没有分配任务的权力,没有考核的权力,没有下命令的权力;SM 是一个协调的角色,保证各个角色及职责的良好协作。

2. Product Owner
PO 在项目组承担了如下细分角色:
(1)领域专家
了解客户、最终用户、以及其他利益相关者对项目的真正需求是什么。
(2)决定需求
负责编写和维护用户需求,并根据商业和市场价值确定需求优先级。
(3)解释需求
负责给项目组成员进行需求答疑。
(4)功能测试
负责编写每个需求的验收标准以及功能测试用例。
(5)验收
在迭代结束时验收开发团队的工作。

PO 可以是来自用户、客户、销售部、产品策划部门或者是开发部门的需求分析人员,无论是来自哪里,需要满足 “CRACK” 的要求:

  • Collaborative:易于协作、易于沟通
  • Representative:能代表用户、客户、市场的利益
  • Authorized:得到了用户、客户、市场等的授权
  • Committed:一诺千金,尽职尽责的工作
  • Knowledgeable:领域专家,行业知识丰富
3. Team
Team 是技术团队,他们负责实现这个系统。Team 是自我管理的,不需要外部的管理者来管理他们。Team 成员应该是“全栈型”技术人员,而不是那种专业化分工的团队,这样才能易于沟通。

Team 承担了如下的细分角色:
(1)设计人员
对系统进行架构设计。
(2)实现人员
负责构建并实现整个系统,并对系统执行单元测试。
(3)管理人员
大家一起来估算、一起来选择任务、一起来跟踪进展情况。

总结:PO 决定做什么,SM 从过程上保证如何实现这个项目,Team 从技术上保证如何实现这个项目。

Scrum_001:什么是 Scrum ?

Scrum 是当前最流行的一种敏捷开发方法论。
Scrum 的本意是橄榄球运动的争球,把一个开发流程的名字取名为 Scrum,是希望在开发软件时,大家像打橄榄球一样富有战斗激情和合作精神。

Scrum 五大核心价值
  1. 专注 Focus
  2. 勇气 Courage 
  3. 开放 Openness
  4. 承诺 Commitment 
  5. 尊重 Respect
Scrum 三大理论基础
  1. 透明性 Transparency
    开发过程的各个环节保持高度的可见性。
  2. 检验 Inspection
    开发过程中的各方面均可检验。
  3. 适应 Adaptation
    发现偏差,及时调整。
1. 什么是敏捷开发?
敏捷开发是一种以人为核心的、迭代增量式的、循序渐进的开发方法。
它是一种软件开发的流程,指导我们用规定的环节去一步一步完成项目的开发;而这种开发方式的主要驱动核心是人;采用迭代式开发,即整个开发过程由若干个短的迭代周期组成。

敏捷开发宣言(Manifesto for Agile Software Development)

  • 个体和互动 高于 流程和工具
    Individuals and interactions over processes and tools 
  • 可以工作的软件 高于 详尽的文档
    Working software over comprehensive documentation
    最好的文档是代码和团队。
    直到迫切需要并且意义重大时,才编写文档。
  • 客户合作 高于 合同谈判
    Customer collaboration over contract negotiation 
  • 响应变化 高于 遵循计划
    Responding to change over following a plan 

也就是说,尽管右项有其价值,我们更重视左项的价值。
That is, while there is value in the items on the right, we value the items on the left more.

2. 为什么敏捷开发是以人为核心的开发方法?
瀑布式开发模型是以文档为驱动的,在其整个开发过程中,需要写大量的文档,一切以文档为依据,开发人员都是根据需求和设计文档开发,测试人员根据测试文档测试。而敏捷开发只写最最必要的文档,它注重的是人与人之间的面对面的交流,所以它强调以人为核心。

3. 什么是迭代(Iteration)
迭代是指把一个复杂且开发周期很长的开发任务,分解为很多小周期可完成的任务,这样的一个周期就是一次迭代的过程。每一次迭代都可以生产出一个可以交付的软件产品。
在 Scrum 中,迭代称为冲刺(Sprint),Sprint 原意是短距离赛跑的意思,也就是说,我们要把一次迭代的开发内容以最快的速度完成它,这个过程我们称它为 Sprint。

切记,敏捷不是目的,敏捷是过程,为了敏捷而敏捷会导致萝卜快了不洗泥,导致 quick and dirty 式的开发。

2018年12月15日星期六

ML_008:Google Machine Learning 在线速成课程学习笔记之八

在线课程地址:https://developers.google.com/machine-learning

11. 分类

11.1 阈值
逻辑回归返回的是概率。为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 0.5,但阈值取决于具体问题,因此您必须对其进行调整。

我们将在后面的部分中详细介绍可用于对分类模型的预测进行评估的指标,以及更改分类阈值对这些预测的影响。

11.2 正分类与负分类
在本部分,我们将定义用于评估分类模型的指标的主要组成部分。
以狼来了故事为例,我们做出以下定义:

  • “狼来了”是正类别。
  • “没有狼”是负类别。


我们可以使用一个 2x2 混淆矩阵来总结我们的“狼预测”模型,该矩阵描述了所有可能出现的结果(共四种):
真正例 (TP):
  • 真实情况:受到狼的威胁。
  • 牧童说:“狼来了。”
  • 结果:牧童是个英雄。
假正例 (FP):
  • 真实情况:没受到狼的威胁。
  • 牧童说:“狼来了。”
  • 结果:村民们因牧童吵醒他们而感到非常生气。
假负例 (FN):
  • 真实情况:受到狼的威胁。
  • 牧童说:“没有狼”。
  • 结果:狼吃掉了所有的羊。
真负例 (TN):
  • 真实情况:没受到狼的威胁。
  • 牧童说:“没有狼”。
  • 结果:大家都没事。

真正例是指模型将正类别样本正确地预测为正类别。
真负例是指模型将负类别样本正确地预测为负类别。
假正例是指模型将负类别样本错误地预测为正类别。
假负例是指模型将正类别样本错误地预测为负类别。

11.3 准确率
让我们来试着计算一下以下模型的准确率,该模型将 100 个肿瘤分为恶性 (正类别)或良性(负类别):
真正例 (TP):
  • 真实情况:恶性
  • 机器学习模型预测的结果:恶性
  • TP 结果数:1
假正例 (FP):
  • 真实情况:良性
  • 机器学习模型预测的结果:恶性
  • FP 结果数:1
假负例 (FN):
  • 真实情况:恶性
  • 机器学习模型预测的结果:良性
  • FN 结果数:8
真负例 (TN):
  • 真实情况:良性
  • 机器学习模型预测的结果:良性
  • TN 结果数:90
准确率为 0.91,即 91%(总共 100 个样本中有 91 个预测正确)。但这是否表示我们的肿瘤分类器在识别恶性肿瘤方面表现得非常出色呢?
实际上,只要我们仔细分析一下正类别和负类别,就可以更好地了解我们模型的效果。在 100 个肿瘤样本中,91 个为良性(90 个 TN 和 1 个 FP),9 个为恶性(1 个 TP 和 8 个 FN)。在 91 个良性肿瘤中,该模型将 90 个正确识别为良性。这很好。不过,在 9 个恶性肿瘤中,该模型仅将 1 个正确识别为恶性。这是多么可怕的结果!9 个恶性肿瘤中有 8 个未被诊断出来!
虽然 91% 的准确率可能乍一看还不错,但如果另一个肿瘤分类器模型总是预测良性,那么这个模型使用我们的样本进行预测也会实现相同的准确率(100 个中有 91 个预测正确)。换言之,我们的模型与那些没有预测能力来区分恶性肿瘤和良性肿瘤的模型差不多
当您使用分类不平衡的数据集(比如正类别标签和负类别标签的数量之间存在明显差异)时,单单准确率一项并不能反映全面情况。
精确率和召回率这两个指标能够更好地评估分类不平衡问题。
11.4 精确率和召回率
精确率尝试回答的问题是:在被识别为正类别的样本中,确实为正类别的比例是多少?
注意:如果模型的预测结果中没有假正例,则模型的精确率为 1.0。
让我们来计算一下肿瘤分类器的精确率:
该模型的精确率为 0.5,也就是说,该模型在预测恶性肿瘤方面的正确率是 50%。

召回率尝试回答的问题是:在所有正类别样本中,被正确识别为正类别的比例是多少?
注意:如果模型的预测结果中没有假负例,则模型的召回率为 1.0。
让我们来计算一下肿瘤分类器的召回率:
该模型的召回率是 0.11,也就是说,该模型能够正确识别出所有恶性肿瘤的百分比是 11%。

精确率和召回率:一场拔河比赛
要全面评估模型的有效性,必须同时检查精确率和召回率。遗憾的是,精确率和召回率往往是此消彼长的情况。也就是说,提高精确率通常会降低召回率值,反之亦然。请观察下图来了解这一概念,该图显示了电子邮件分类模型做出的 30 项预测。分类阈值右侧的被归类为“垃圾邮件”,左侧的则被归类为“非垃圾邮件”。
从 0 到 1.0 的数轴,其中放置了 30 个样本。
从 0 到 1.0 的数轴,其中放置了 30 个样本。
根据上图将电子邮件归类为垃圾邮件或非垃圾邮件。
真正例 (TP):8假正例 (FP):2
假负例 (FN):3真负例 (TN):17
精确率指的是被标记为垃圾邮件的电子邮件中正确分类的电子邮件所占的百分比,即上图中阈值线右侧的绿点所占的百分比:0.8
召回率指的是实际垃圾邮件中正确分类的电子邮件所占的百分比,即上图中阈值线右侧的绿点所占的百分比:0.73

一组相同的样本,但分类阈值略有提高。

一组相同的样本,但分类阈值略有提高。30 个样本中的 2 个已经过重新分类。
提高分类阈值,假正例数量会减少,假负例数量会增加。结果,精确率有所提高:0.88,而召回率则有所降低:0.64。

一组相同的样本,但分类阈值有所降低。
一组相同的样本,但分类阈值有所降低。
降低分类阈值,假正例数量会增加,假负例数量会减少。结果,精确率有所降低:0.75,而召回率则有所提高:0.82。

11.5 检查理解准确率、精确率和召回率

问题1:一只造价昂贵的机器鸡每天要穿过一条交通繁忙的道路一千次。某个机器学习模型评估交通模式,预测这只鸡何时可以安全穿过街道,准确率为 99.99%。
分析1:在一条交通繁忙的道路上,99.99% 的准确率充分表明该机器学习模型的作用比碰运气要好得多。不过,在某些情况下,即使偶尔出现错误,代价也相当高。99.99% 的准确率意味着这只昂贵的鸡平均每 10 天就要更换一次。(这只鸡也可能对它撞到的汽车造成严重损坏。)

问题2:一种致命但可治愈的疾病影响着 0.01% 的人群。某个机器学习模型使用其症状作为特征,预测这种疾病的准确率为 99.99%。
分析2:在这种情形中,准确率是个糟糕的指标。毕竟,即使它只是个一律预测“没病”的“愚蠢”模型,也依然能达到 99.99% 的准确率。而将某个患病的人错误地预测为“没病”则可能是致命的。

问题3:在 roulette 游戏中,一只球会落在旋转轮上,并且最终落入 38 个槽的其中一个内。某个机器学习模型可以使用视觉特征(球的旋转方式、球落下时旋转轮所在的位置、球在旋转轮上方的高度)预测球会落入哪个槽中,准确率为 4%。
分析3:这个机器学习模型做出的预测比碰运气要好得多;随机猜测的正确率为 1/38,即准确率为 2.6%。尽管该模型的准确率“只有”4%,但成功预测获得的好处远远大于预测失败的损失。

11.6 ROC 曲线
ROC 曲线(接收者操作特征曲线)是一种显示分类模型在所有分类阈值下的效果的图表。该曲线绘制了以下两个参数:
  • 真正例率 (TPR)
  • 假正例率 (FPR)
ROC 曲线用于绘制采用不同分类阈值时的 TPR 与 FPR。降低分类阈值会导致将更多样本归为正类别,从而增加假正例和真正例的个数。下图显示了一个典型的 ROC 曲线。
ROC 曲线中的一条曲线显示了不同分类阈值下的 TP 率与 FP 率。
为了计算 ROC 曲线上的点,我们可以使用不同的分类阈值多次评估逻辑回归模型,但这样做效率非常低。幸运的是,有一种基于排序的高效算法可以为我们提供此类信息,这种算法称为曲线下面积。

曲线下面积:ROC 曲线下面积
曲线下面积表示“ROC 曲线下面积”。也就是说,曲线下面积测量的是从 (0,0) 到 (1,1) 之间整个 ROC 曲线以下的整个二维面积(参考积分学)。
AUC(ROC 曲线以下的面积)。

曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。以下面的样本为例,逻辑回归预测从左到右以升序排列:
正分类和负分类样本按逻辑回归分数以升序排列。
正分类和负分类样本按逻辑回归分数以升序排列。
图 6. 预测按逻辑回归分数以升序排列。

曲线下面积表示随机正类别(绿色)样本位于随机负类别(红色)样本右侧的概率

曲线下面积的取值范围为 0-1。预测结果 100% 错误的模型的曲线下面积为 0.0;而预测结果 100% 正确的模型的曲线下面积为 1.0。

曲线下面积因以下两个原因而比较实用:
  • 曲线下面积的尺度不变。它测量预测的排名情况,而不是测量其绝对值。
  • 曲线下面积的分类阈值不变。它测量模型预测的质量,而不考虑所选的分类阈值。
不过,这两个原因都有各自的局限性,这可能会导致曲线下面积在某些用例中不太实用:
  • 并非总是希望尺度不变。 例如,有时我们非常需要被良好校准的概率输出,而曲线下面积无法告诉我们这一结果。
  • 并非总是希望分类阈值不变。 在假负例与假正例的代价存在较大差异的情况下,尽量减少一种类型的分类错误可能至关重要。例如,在进行垃圾邮件检测时,您可能希望优先考虑尽量减少假正例(即使这会导致假负例大幅增加)。对于此类优化,曲线下面积并非一个实用的指标。
11.7 预测偏差
逻辑回归预测应当无偏差。(为什么?)即“预测平均值”应当约等于“观察平均值”,预测偏差指的是这两个平均值之间的差值。
如果出现非常高的非零预测偏差,则说明模型某处存在错误,因为这表明模型对正类别标签的出现频率预测有误。

例如,假设我们知道,所有电子邮件中平均有 1% 的邮件是垃圾邮件。如果我们对某一封给定电子邮件一无所知,则预测它是垃圾邮件的可能性为 1%。同样,一个出色的垃圾邮件模型应该预测到电子邮件平均有 1% 的可能性是垃圾邮件。(换言之,如果我们计算单个电子邮件是垃圾邮件的预测可能性的平均值,则结果应该是 1%。)然而,如果该模型预测电子邮件是垃圾邮件的平均可能性为 20%,那么我们可以得出结论,该模型出现了预测偏差。

造成预测偏差的可能原因包括:

特征集不完整
数据集混乱
模型实现流水线中有错误?
训练样本有偏差
正则化过强
您可能会通过对学习模型进行后期处理来纠正预测偏差,即通过添加校准层来调整模型的输出,从而减小预测偏差。例如,如果您的模型存在 3% 以上的偏差,则可以添加一个校准层,将平均预测偏差降低 3%。但是,添加校准层并非良策,具体原因如下:

您修复的是症状,而不是原因。
您建立了一个更脆弱的系统,并且必须持续更新。
如果可能的话,请避免添加校准层。使用校准层的项目往往会对其产生依赖 - 使用校准层来修复模型的所有错误。最终,维护校准层可能会令人苦不堪言。

注意:出色模型的偏差通常接近于零。即便如此,预测偏差低并不能证明您的模型比较出色。特别糟糕的模型的预测偏差也有可能为零。例如,只能预测所有样本平均值的模型是糟糕的模型,尽管其预测偏差为零。
分桶偏差和预测偏差
逻辑回归可预测 0 到 1 之间的值。不过,所有带标签样本都正好是 0(例如,0 表示“非垃圾邮件”)或 1(例如,1 表示“垃圾邮件”)。因此,在检查预测偏差时,您无法仅根据一个样本准确地确定预测偏差;您必须在“一大桶”样本中检查预测偏差。也就是说,只有将足够的样本组合在一起以便能够比较预测值(例如 0.392)与观察值(例如 0.394),逻辑回归的预测偏差才有意义。

您可以通过以下方式构建桶:

以线性方式分解目标预测。
构建分位数。
请查看以下某个特定模型的校准曲线。每个点表示包含 1000 个值的分桶。两个轴具有以下含义:

x 轴表示模型针对该桶预测的平均值。
y 轴表示该桶的数据集中的实际平均值。
两个轴均采用对数尺度。

x 轴表示预测值;y 轴表示带标签值。对于中等和较高的预测值,预测偏差可以忽略不计。对于较低的预测值,预测偏差相对较高。
图 8. 预测偏差曲线(对数尺度)

为什么只有模型的某些部分所做的预测如此糟糕?以下是几种可能性:

训练集不能充分表示数据空间的某些子集。
数据集的某些子集比其他子集更混乱。
该模型过于正则化。(不妨减小 lambda 的值。)

2018年12月14日星期五

ML_007:Google Machine Learning 在线速成课程学习笔记之七

在线课程地址:https://developers.google.com/machine-learning

10. 逻辑回归

10.1 计算概率
思考一个问题:抛出一个略微弯曲的硬币,硬币正面朝上的概率是多少?

逻辑回归会生成一个介于 0 到 1 之间(不包括 0 和 1)的概率值,而不是确切地预测结果是 0 还是 1。以用于检测垃圾邮件的逻辑回归模型为例。如果此模型推断某一特定电子邮件的值为 0.932,则意味着该电子邮件是垃圾邮件的概率为 93.2%。更准确地说,这意味着在无限训练样本的极限情况下,模型预测其值为 0.932 的这组样本实际上有 93.2% 是垃圾邮件,其余的 6.8% 不是垃圾邮件。

许多问题需要将概率估算值作为输出。逻辑回归是一种极其高效的概率计算机制。实际上,您可以通过下两种方式之一使用返回的概率:
  • “按原样”
  • 转换成二元类别。
我们来了解一下如何“按原样”使用概率。假设我们创建一个逻辑回归模型来预测狗在半夜发出叫声的概率。我们将此概率称为:p(bark | night)
如果逻辑回归模型预测 p(bark | night) 的值为 0.05,那么一年内,狗的主人应该被惊醒约 18 次:
  startled = p(bark | night) * nights
  18 ~= 0.05 * 365

在很多情况下,您会将逻辑回归输出映射到二元分类问题的解决方案,该二元分类问题的目标是正确预测两个可能的标签(例如,“垃圾邮件”或“非垃圾邮件”)中的一个。

您可能想知道逻辑回归模型如何确保输出值始终落在 0 和 1 之间。巧合的是,S 型函数生成的输出值正好具有这些特性,其定义如下:

S 型函数的曲线图如下:
S 型函数。x 轴表示原始推断值。y 轴从 0 扩展到 +1(不包括 0 和 +1)。
如果 z 表示使用逻辑回归训练的模型的线性层的输出,则 S 型(z) 函数会生成一个介于 0 和 1 之间的值(概率)。

10.2 模型训练
线性回归的损失函数是平方损失。逻辑回归的损失函数是对数损失函数。

正则化在逻辑回归建模中极其重要。如果没有正则化,逻辑回归的渐近性会不断促使损失在高维度空间内达到 0。因此,大多数逻辑回归模型会使用以下两个策略之一来降低模型复杂性:

  • L2 正则化。
  • 早停法,限制训练步数或学习速率。

假设您向每个样本分配一个唯一 ID,且将每个 ID 映射到其自己的特征。如果您未指定正则化函数,模型会变得完全过拟合。这是因为模型会尝试促使所有样本的损失达到 0 ,但始终达不到,从而使每个指示器特征的权重接近正无穷或负无穷。当有大量罕见的特征组合且每个样本中仅一个时,包含特征组合的高维度数据会出现这种情况。


幸运的是,使用 L2 或早停法可以防止出现此类问题。

2018年11月27日星期二

ML_006:Google Machine Learning 在线速成课程学习笔记之六

在线课程地址:https://developers.google.com/machine-learning

9. 正则化
为什么要正则化?简单来说,就是不要过于依赖样本,防止过拟合。

9.1 L2 正则化
请查看以下泛化曲线,该曲线显示的是训练集和验证集相对于训练迭代次数的损失。
训练集的损失函数逐渐下降。相比之下,验证集的损失函数先下降,然后开始上升。
上图显示的是某个模型的训练损失逐渐减少,但验证损失最终增加。换言之,该泛化曲线显示该模型与训练集中的数据过拟合。
根据奥卡姆剃刀定律,或许我们可以通过降低复杂模型的复杂度来防止过拟合,这种原则称为正则化
也就是说,并非只是以最小化损失(经验风险最小化)为目标,而是以最小化损失和复杂度为目标,这称为结构风险最小化
现在,我们的训练优化算法是一个由两项内容组成的函数:一个是损失项,用于衡量模型与数据的拟合度,另一个是正则化项,用于衡量模型复杂度。

机器学习速成课程重点介绍了两种衡量模型复杂度的常见方式(这两种方式有些相关):
  • 将模型复杂度作为模型中所有特征的权重的函数。
  • 将模型复杂度作为具有非零权重的特征总数的函数。
如果模型复杂度是权重的函数,则特征权重的绝对值越高,对模型复杂度的贡献就越大。
我们可以使用 L2 正则化公式来量化复杂度,该公式将正则化项定义为所有特征权重的平方和。
在这个公式中,接近于 0 的权重对模型复杂度几乎没有影响,而离群值权重则可能会产生巨大的影响。

9.2 简化正则化 Lambda

模型开发者通过以下方式来调整正则化项的整体影响:用正则化项的值乘以名为 lambda(又称为正则化率)的标量。也就是说,模型开发者会执行以下运算:

执行简化正则化对模型具有以下影响:(为什么?
  • 使权重值接近于 0(但并非正好为 0)
  • 使权重的平均值接近于 0,且呈正态(钟形曲线或高斯曲线)分布。
增加 lambda 值将增强正则化效果,lambda 值较高的权重直方图可能会下图所示:
模型权重的平均值为 0 且权重呈正态分布的直方图。
降低 lambda 的值往往会得出比较平缓的直方图,如下图所示:
模型权重的平均值为 0 且权重分布方式介于均匀分布和正态分布之间的直方图。
在选择 lambda 值时,目标是在简单化和训练数据拟合之间达到适当的平衡:
  • 如果您的 lambda 值过高,则模型会非常简单,但是您将面临数据欠拟合的风险。您的模型将无法从训练数据中获得足够的信息来做出有用的预测。
  • 如果您的 lambda 值过低,则模型会比较复杂,并且您将面临数据过拟合的风险。您的模型将因获得过多训练数据特点方面的信息而无法泛化到新数据。
注意:将 lambda 设为 0 可彻底取消正则化。 在这种情况下,训练的唯一目的将是最小化损失,而这样做会使过拟合的风险达到最高。

理想的 lambda 值生成的模型可以很好地泛化到以前未见过的新数据。遗憾的是,理想的 lambda 值取决于数据,因此您需要手动或自动进行一些调整。
lambda 值是怎么得到的?

2018年11月26日星期一

ML_005:Google Machine Learning 在线速成课程学习笔记之五

在线课程地址:https://developers.google.com/machine-learning

8. 特征组合 

8.1 对非线性规律进行编码
下图中,蓝点代表生病的树,橙点代表健康的树。
蓝点占据东北和西南象限;橙点占据西北和东南象限。
您可以画一条直线将生病的树与健康的树清晰地分开吗?不,您做不到。这是个非线性问题。您画的任何一条线都不能很好地预测树的健康状况。
要解决上图所示的非线性问题,可以创建一个特征组合。特征组合是指通过将两个或多个输入特征相乘来对特征空间中的非线性规律进行编码的合成特征。“cross”(组合)这一术语来自 cross product(向量积)。我们通过将 x1 与x2 组合来创建一个名为 x3 的特征组合:
x3=x1*x2
我们像处理任何其他特征一样来处理这个新建的  特征组合。线性公式变为:
y = b + w1*x1 + w2*x2 + w3*x3
线性算法可以算出 w3 权重值,就像算出 w1 和 w2 权重值一样。换言之,虽然 w3 表示非线性信息,但您不需要改变线性模型的训练方式来确定 w3 的值。

特征组合的种类
我们可以创建很多不同种类的特征组合。例如:
  • [A X B]:将两个特征的值相乘形成的特征组合。
  • [A x B x C x D x E]:将五个特征的值相乘形成的特征组合。
  • [A x A]:对单个特征的值求平方形成的特征组合。
通过采用随机梯度下降法,可以有效地训练线性模型。因此,在使用扩展的线性模型时辅以特征组合一直都是训练大规模数据集的有效方法。

8.2 组合独热矢量
在实践中,机器学习模型很少会组合两个单独的浮点特征。机器学习模型经常组合独热特征矢量,将独热特征矢量的特征组合视为逻辑连接。
例如,假设我们具有以下两个特征:国家/地区和语言。对每个特征进行独热编码会生成具有二元特征的矢量,这些二元特征可解读为 country=USA, country=France 或 language=English, language=Spanish。然后,如果您对这些独热编码进行特征组合,则会得到可解读为逻辑连接的二元特征,如下所示:
  country:usa AND language:spanish
再举一个例子,假设您对纬度和经度进行分箱,获得单独的独热 5 元素特征矢量。例如,指定的纬度和经度可以表示如下:
  binned_latitude = [0, 0, 0, 1, 0]
  binned_longitude = [0, 1, 0, 0, 0]
假设您对这两个特征矢量创建了特征组合:
  binned_latitude X binned_longitude
此特征组合是一个 25 元素独热矢量(24 个 0 和 1 个 1)。该组合中的单个 1 表示纬度与经度的特定连接。然后,您的模型就可以了解到有关这种连接的特定关联性。

假设我们更粗略地对纬度和经度进行分箱,如下所示:

binned_latitude(lat) = [
  0  < lat <= 10
  10 < lat <= 20
  20 < lat <= 30
]

binned_longitude(lon) = [
  0  < lon <= 15
  15 < lon <= 30
]

针对这些粗略分箱创建特征组合会生成具有以下含义的合成特征:

binned_latitude_X_longitude(lat, lon) = [
  0  < lat <= 10 AND 0  < lon <= 15
  0  < lat <= 10 AND 15 < lon <= 30
  10 < lat <= 20 AND 0  < lon <= 15
  10 < lat <= 20 AND 15 < lon <= 30
  20 < lat <= 30 AND 0  < lon <= 15
  20 < lat <= 30 AND 15 < lon <= 30
]
现在,假设我们的模型需要根据以下两个特征来预测狗主人对狗狗的满意程度:
  • 行为类型(吠叫、叫、偎依等)
  • 时段
如果我们根据这两个特征构建以下特征组合:
  [behavior type X time of day]
我们最终获得的预测能力将远远超过任一特征单独的预测能力。
例如,如果狗狗在下午 5 点主人下班回来时(快乐地)叫喊,可能表示对主人满意度的正面预测结果。如果狗狗在凌晨 3 点主人熟睡时(也许痛苦地)哀叫,可能表示对主人满意度的强烈负面预测结果。

线性学习器可以很好地扩展到大量数据。对大规模数据集使用特征组合是学习高度复杂模型的一种有效策略。神经网络可提供另一种策略,将在后文介绍。




ML_004:Google Machine Learning 在线速成课程学习笔记之四

在线课程地址:https://developers.google.com/machine-learning

7. 表示法

7.1 特征工程
传统编程的关注点是代码。在机器学习项目中,关注点变成了表示。也就是说,开发者通过添加和改善特征来调整模型。

将原始数据映射到特征
原始数据通过名为特征工程的程序映射到特征矢量。
上图左侧表示来自输入数据源的原始数据,右侧表示特征矢量,也就是组成数据集中样本的浮点值集。 特征工程指的是将原始数据转换为特征矢量。进行特征工程预计需要大量时间。

机器学习模型通常期望样本表示为实数矢量。这种矢量的构建方法如下:为每个字段衍生特征,然后将它们全部连接到一起。

映射数值
机器学习模型根据浮点值进行训练,因此整数和浮点原始数据不需要特殊编码。
将原始整数 (6) 映射到浮点特征 (6.0)。

映射字符串值
模型无法通过字符串值学习规律,因此您需要进行一些特征工程来将这些值转换为数字形式:

首先,为您要表示的所有特征的字符串值定义一个词汇表。对于 street_name 特征,该词汇表中将包含您知道的所有街道。
然后,使用该词汇表创建一个独热编码,用于将指定字符串值表示为二元矢量。在该矢量(与指定的字符串值对应)中:

  • 只有一个元素设为 1。
  • 其他所有元素均设为 0。
  • 该矢量的长度等于词汇表中的元素数。

通过独热编码映射字符串值。
上图显示了某条特定街道 (Shorebird Way) 的独热编码。在此二元矢量中,代表 Shorebird Way 的元素的值为 1,而代表所有其他街道的元素的值为 0。

映射分类(枚举)值
分类特征具有一组离散的可能值。例如,名为 Lowland Countries 的特征只包含 3 个可能值: {'Netherlands', 'Belgium', 'Luxembourg'}
您可能会将分类特征(如 Lowland Countries)编码为枚举类型或表示不同值的整数离散集。例如:
  • 将荷兰表示为 0
  • 将比利时表示为 1
  • 将卢森堡表示为 2
不过,机器学习模型通常将每个分类特征表示为单独的布尔值。例如,Lowland Countries 在模型中可以表示为 3 个单独的布尔值特征:
  • x1:是荷兰吗?
  • x2:是比利时吗?
  • x3:是卢森堡吗?
采用这种方法编码还可以简化某个值可能属于多个分类这种情况(例如,“与法国接壤”对于比利时和卢森堡来说都是 True)。

7.2 良好特征的特点

避免很少使用的离散特征值
良好的特征值应该在数据集中出现大约 5 次以上。这样一来,模型就可以学习该特征值与标签是如何关联的。也就是说,大量离散值相同的样本可让模型有机会了解不同设置中的特征,从而判断何时可以对标签很好地做出预测。
例如,house_type 特征可能包含大量样本,其中它的值为 victorian。
相反,如果某个特征的值仅出现一次或者很少出现,则模型就无法根据该特征进行预测。
例如,unique_house_id 就不适合作为特征,因为每个值只使用一次,模型无法从中学习任何规律。

最好具有清晰明确的含义
每个特征对于项目中的任何人来说都应该具有清晰明确的含义。

不要将“神奇”的值与实际数据混为一谈
良好的浮点特征不包含超出范围的异常断点或“神奇”的值。
例如,假设一个特征具有 0 到 1 之间的浮点值。那么,如下值是可以接受的:quality_rating: 0.37。
如果用户没有输入 quality_rating,则数据集可能使用如下神奇值来表示不存在该值:
quality_rating: -1。

为解决神奇值的问题,需将该特征转换为两个特征:

  • 一个特征只存储质量评分,不含神奇值。
  • 一个特征存储布尔值,表示是否提供了 quality_rating。为该布尔值特征指定一个名称,例如 is_quality_rating_defined。

考虑上游不稳定性
特征的定义不应随时间发生变化。例如,下列值是有用的,因为城市名称一般不会改变,example:city_id: "br/sao_paulo"
但收集由其他模型推理的值会产生额外成本。可能值“219”目前代表圣保罗,但这种表示在未来运行其他模型时可能轻易发生变化:inferred_city_cluster: "219"。

7.3 数据清理
作为一名机器学习工程师,您将花费大量的时间挑出坏样本并加工可以挽救的样本。即使是非常少量的“坏苹果”也会破坏掉一个大规模数据集。

缩放特征值
缩放是指将浮点特征值从自然范围(例如 100 到 900)转换为标准范围(例如 0 到 1 或 -1 到 +1)。如果某个特征集只包含一个特征,则缩放可以提供的实际好处微乎其微或根本没有。不过,如果特征集包含多个特征,则缩放特征可以带来以下优势:
  • 帮助梯度下降法更快速地收敛。
  • 帮助避免“NaN 陷阱”。在这种陷阱中,模型中的一个数值变成 NaN(例如,当某个值在训练期间超出浮点精确率限制时),并且模型中的所有其他数值最终也会因数学运算而变成 NaN。
  • 帮助模型为每个特征确定合适的权重。如果没有进行特征缩放,则模型会对范围较大的特征投入过多精力。
您不需要对每个浮点特征进行完全相同的缩放。即使特征 A 的范围是 -1 到 +1,同时特征 B 的范围是 -3 到 +3,也不会产生什么恶劣的影响。不过,如果特征 B 的范围是 5000 到 100000,您的模型会出现糟糕的响应。

处理极端离群值
如何最大限度降低这些极端离群值的影响?
一种方法是对每个值取对数。对数缩放可稍稍缓解这种影响,但仍然存在离群值这个大尾巴。
对此,我们采用另一种简单的方法:将某个特征的最大值“限制”为某个任意值,所有大于该值的值都将变为该值。

分箱
下图显示了加利福尼亚州不同纬度的房屋相对普及率。
每个纬度的房屋数曲线图。曲线图极其不规则,在纬度 36 左右出现低谷,并在纬度 34 和 38 左右出现巨大峰值。
在数据集中,latitude 是一个浮点值。模型中将 latitude 表示为浮点特征没有意义。这是因为纬度和房屋价值之间不存在线性关系。例如,纬度 35 处的房屋并不比纬度 34 处的房屋贵 35/34(或更便宜)。但是,纬度或许能很好地预测房屋价值。
为了将纬度变为一项实用的预测指标,我们对纬度“分箱”,如下图所示:
每个纬度的房屋数曲线图。曲线图分为
每个纬度的房屋数曲线图。
我们现在拥有 11 个不同的布尔值特征(LatitudeBin1、LatitudeBin2、…、LatitudeBin11),而不是一个浮点特征。拥有 11 个不同的特征有点不方便,因此我们将它们统一成一个 11 元素矢量。这样做之后,我们可以将纬度 37.4 表示为:
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
分箱之后,我们的模型现在可以为每个纬度学习完全不同的权重。

清查
截至目前,我们假定用于训练和测试的所有数据都是值得信赖的。在现实生活中,数据集中的很多样本是不可靠的,原因有以下一种或多种:
  • 遗漏值。 例如,有人忘记为某个房屋的年龄输入值。
  • 重复样本。 例如,服务器错误地将同一条记录上传了两次。
  • 不良标签。 例如,有人错误地将一颗橡树的图片标记为枫树。
  • 不良特征值。 例如,有人输入了多余的位数,或者温度计被遗落在太阳底下。
一旦检测到存在这些问题,您通常需要将相应样本从数据集中移除,从而“修正”不良样本。要检测遗漏值或重复样本,您可以编写一个简单的程序。检测不良特征值或标签可能会比较棘手。
除了检测各个不良样本之外,您还必须检测集合中的不良数据。直方图是一种用于可视化集合中数据的很好机制。此外,收集如下统计信息也会有所帮助:
  • 最大值和最小值
  • 均值和中间值
  • 标准偏差
考虑生成离散特征的最常见值列表。例如,country:uk 的样本数是否符合您的预期?language:jp 是否真的应该作为您数据集中的最常用语言?

了解数据
遵循以下规则:
  • 记住您预期的数据状态。
  • 确认数据是否满足这些预期(或者您可以解释为何数据不满足预期)。
  • 仔细检查训练数据是否与其他来源(例如信息中心)的数据一致。
像处理任何任务关键型代码一样谨慎处理您的数据。良好的机器学习依赖于良好的数据。

2018年11月23日星期五

ML_003:Google Machine Learning 在线速成课程学习笔记之三

在线课程地址:https://developers.google.com/machine-learning

4. 泛化
泛化是指模型很好地拟合以前未见过的新数据(从用于创建该模型的同一分布中抽取)的能力。

过拟合模型在训练过程中产生的损失很低,但在预测新数据方面的表现却非常糟糕。过拟合是由于模型的复杂程度超出所需程度而造成的。

机器学习的目标是对从真实概率分布(已隐藏)中抽取的新数据做出良好预测。遗憾的是,模型无法查看整体情况;模型只能从训练数据集中取样。如果某个模型在拟合当前样本方面表现良好,那么我们如何相信该模型会对新数据做出良好的预测呢?

奥卡姆剃刀定律:机器学习模型越简单,实证结果就越有可能不仅仅基于样本的特性。

根据以下因素描述模型泛化到新数据的能力:
  • 模型的复杂程度
  • 模型在处理训练数据方面的表现
如何获得以前未见过的数据呢?一种方法是将您的数据集分成两个子集:
  • 训练集 - 用于训练模型的子集。
  • 测试集 - 用于测试模型的子集。
机器学习细则
以下三项基本假设阐明了泛化:
  • 我们从分布中随机抽取独立同分布的样本。
  • 分布是平稳的;即分布在数据集内不会发生变化。
  • 我们从同一分布的数据划分中抽取样本。
在实践中,我们有时会违背这些假设。例如:
想象有一个选择要展示的广告的模型。如果该模型在某种程度上根据用户以前看过的广告选择广告,则会违背假设。
想象有一个包含一年零售信息的数据集。用户的购买行为会出现季节性变化,这会违反平稳性。

如果违背了上述三项基本假设中的任何一项,那么我们就必须密切注意指标。

总结
如果某个模型尝试紧密拟合训练数据,但却不能很好地泛化到新数据,就会发生过拟合。
如果不符合监督式机器学习的关键假设,那么我们将失去对新数据进行预测这项能力的重要理论保证。

5. 训练集和测试集
将数据集分为两个子集的概念:

  • 训练集 - 用于训练模型的子集。
  • 测试集 - 用于测试训练后模型的子集。

确保您的测试集满足以下两个条件:

  • 规模足够大,可产生具有统计意义的结果。
  • 能代表整个数据集。换言之,挑选的测试集的特征应该与训练集的特征相同。

请勿对测试数据进行训练。如果您的评估指标取得了意外的好结果,则可能表明您不小心对测试集进行了训练。

例如,假设一个模型要预测某封电子邮件是否是垃圾邮件,它使用主题行、邮件正文和发件人的电子邮件地址作为特征。我们按照 80-20 的拆分比例将数据拆分为训练集和测试集。在训练之后,该模型在训练集和测试集上均达到了 99% 的精确率。我们原本预计测试集上的精确率会低于此结果,因此再次查看数据后发现,测试集中的很多样本与训练集中的样本是重复的(由于疏忽,我们在拆分数据之前,没有将输入数据库中的相同垃圾邮件重复条目清理掉)。我们无意中对一些测试数据进行了训练,因此无法再准确衡量该模型泛化到新数据的效果。

6. 验证集
为了防止过拟合,增加了验证集。
水平条分为三段:70% 属于训练集,15% 属于验证集,15% 属于测试集
该工作流程之所以更好,原因在于它暴露给测试集的信息更少。
与图 2 类似的工作流程,不同之处在于该工作流程使用验证集而不是测试集来评估模型。然后,在训练集和验证集大致达成一致后,使用测试集确认模型效果。

ML_002:Google Machine Learning 在线速成课程学习笔记之二

在线课程地址:https://developers.google.com/machine-learning

3. 深入了解机器学习
下图是蝉每分钟的鸣叫声和温度方面的数据关系:
每分钟啁啾(x轴)与温度(y轴)的原始数据。
毫无疑问,这是一个线性关系:
建立每分钟啁啾(x轴)与温度(y轴)关系的最佳线。
如何确定模型的优劣?
简单来说,训练模型表示通过有标签样本来学习(确定)所有权重和偏差的理想值。在监督式学习中,机器学习算法通过以下方式构建模型:检查多个样本并尝试找出可最大限度地减少损失的模型;这一过程称为经验风险最小化。
两个直角坐标曲线图,每个曲线图显示一条线和一些数据点。在第一个曲线图中,线与数据极其不吻合,所以损失较大。在第二个曲线图中,线与数据比较吻合,所以损失较小。
均方误差 (MSE) 指的是每个样本的平均平方损失。
要计算 MSE,请求出各个样本的所有平方损失之和,然后除以样本数量。
虽然 MSE 常用于机器学习,但它既不是唯一实用的损失函数,也不是适用于所有情形的最佳损失函数。

3.1 降低损失:迭代方法
为了训练模型,我们需要一种可降低模型损失的好方法。迭代方法是一种广泛用于降低损失的方法,而且使用起来简单有效。
从特征和标签到模型和预测的循环。
迭代策略在机器学习中的应用非常普遍,这主要是因为它们可以很好地扩展到大型数据集。
“模型”部分将一个或多个特征作为输入,然后返回一个预测值作为输出。
应该设置哪些初始值?对于线性回归问题,事实证明初始值并不重要,可以随机选择值。

最后,我们来看图的“计算参数更新”部分。机器学习系统就是在此部分检查损失函数的值,并生成新的特征权重值。现在,假设这个神秘的绿色框会产生新值,然后机器学习系统将根据所有标签重新评估所有特征,为损失函数生成一个新值,而该值又产生新的参数值。这种学习过程会持续迭代,直到该算法发现损失可能最低的模型参数。通常,您可以不断迭代,直到总体损失不再变化或至少变化极其缓慢为止。这时候,我们可以说该模型已收敛
假设我们有时间和计算资源来计算权重的所有可能值的损失。对于我们一直在研究的回归问题,所产生的损失与权重的图形始终是凸形。换言之,图形始终是碗状图,如下所示:


U 形曲线上的第二个点,这个点更接近最低点。
凸形问题只有一个最低点;即只存在一个斜率正好为 0 的位置。这个最小值就是损失函数收敛之处。

3.2 梯度下降法
通过计算整个数据集中  每个可能值的损失函数来找到收敛点这种方法效率太低。我们来研究一种更好的机制,这种机制在机器学习领域非常热门,称为梯度下降法
请注意,梯度是一个矢量,因此具有以下两个特征:方向和大小。
梯度始终指向损失函数中增长最为迅猛的方向。梯度下降法算法会沿着负梯度的方向走一步,以便尽快降低损失。
U 形曲线上的第二个点,这个点更接近最低点。


3.3 降低损失:学习速率
梯度下降法算法用梯度乘以一个称为学习速率(有时也称为步长)的标量,以确定下一个点的位置。
超参数是编程人员在机器学习算法中用于调整的旋钮。大多数机器学习编程人员会花费相当多的时间来调整学习速率。如果您选择的学习速率过小,就会花费太长的学习时间:
如何确定“金发姑娘”学习速率呢?
在实践中,成功的模型训练并不意味着要找到“完美”(或接近完美)的学习速率。我们的目标是找到一个足够高的学习速率,该速率要能够使梯度下降过程高效收敛,但又不会高到使该过程永远无法收敛。
相同的 U 形曲线。点的轨迹大约需要 8 步达到最低点。

3.4 降低损失:随机梯度下降法
在梯度下降法中,批量指的是用于在单次迭代中计算梯度的样本总数。到目前为止,我们一直假定批量是指整个数据集。就 Google 的规模而言,数据集通常包含数十亿甚至数千亿个样本。此外,Google 数据集通常包含海量特征。因此,一个批量可能相当巨大。如果是超大批量,则单次迭代就可能要花费很长时间进行计算。

包含随机抽样样本的大型数据集可能包含冗余数据。实际上,批量大小越大,出现冗余的可能性就越高。一些冗余可能有助于消除杂乱的梯度,但超大批量所具备的预测价值往往并不比大型批量高

如果我们可以通过更少的计算量得出正确的平均梯度,会怎么样?通过从我们的数据集中随机选择样本,我们可以通过小得多的数据集估算(尽管过程非常杂乱)出较大的平均值。 随机梯度下降法 (SGD) 将这种想法运用到极致,它每次迭代只使用一个样本(批量大小为 1)。如果进行足够的迭代,SGD 也可以发挥作用,但过程会非常杂乱。“随机”这一术语表示构成各个批量的一个样本都是随机选择的。

每次只随机选出一个样本来计算梯度?只要迭代次数够多,也可以快速收敛。

小批量随机梯度下降法(小批量 SGD)是介于全批量迭代与 SGD 之间的折衷方案。小批量通常包含 10-1000 个随机选择的样本。小批量 SGD 可以减少 SGD 中的杂乱样本数量,但仍然比全批量更高效。

为了简化说明,我们只针对单个特征重点介绍了梯度下降法。请放心,梯度下降法也适用于包含多个特征的特征集。

ML_001:Google Machine Learning 在线速成课程学习笔记之一

在线课程地址:https://developers.google.com/machine-learning

1. Machine Learning 简介
为什么要学习 Machine Learning?
(1)ML 是一个可以缩短编程时间的工具
例如:编写一个纠正拼写错误的程序
方式一:根据经验法则编写纠正逻辑。
方式二:使用 ML,只需提供足够的样本数据,就可以编写一个不错的程序。
(2)利用 ML 自定义产品,使其适合特定的用户群
例如:英文拼写纠正的程序很成功,如何提供其他语言拼写纠正程序?
方式一:根据经验法则,需要重新编写其他语言纠正逻辑。
方式二:使用 ML,只需提供其他语言的样本数据即可。
(3)让程序做以前需要人工才能做的事情
例如:人脸识别。我不需要告诉机器如何识别人脸,只需提供数据训练。

学习 ML,还有一个哲学上的意义,ML 可以改变我们的思考问题的方式。
以前的软件工程师使用“数学+逻辑”来解决问题,而 ML 让我们从数学科学转到自然科学,使用统计信息而非逻辑分析来解决问题。

2. 框架处理
引子:如何判断邮件是否是垃圾邮件?
术语列表:
(1)监督式机器学习:机器学习系统通过学习如何组合输入信息来对从未见过的数据做出有用的预测。
(2)标签:标签是我们要预测的事物,即简单线性回归中的 y 变量。标签可以是小麦未来的价格、图片中显示的动物品种、音频剪辑的含义或任何事物。
(3)特征:特征是输入变量,即简单线性回归中的 x 变量。简单的机器学习项目可能会使用单个特征,而比较复杂的机器学习项目可能会使用数百万个特征。
在垃圾邮件检测器示例中,特征可能包括:
  •   电子邮件文本中的字词
  •   发件人的地址
  •   发送电子邮件的时段
  •   电子邮件中包含“一种奇怪的把戏”这样的短语。
(4)样本:样本是指数据的特定实例。
样本分为以下两类:有标签样本和无标签样本。我们使用有标签样本来训练模型。
在垃圾邮件检测器示例中,有标签样本是用户明确标记为“垃圾邮件”或“非垃圾邮件”的各个电子邮件。
(5)模型
模型定义了特征与标签之间的关系。
例如,垃圾邮件检测模型可能会将某些特征与“垃圾邮件”紧密联系起来。
模型生命周期的两个阶段:
  • 训练表示创建或学习模型。
    也就是说,您向模型展示有标签样本,让模型逐渐学习特征与标签之间的关系。
  • 推断表示将训练后的模型应用于无标签样本。
    也就是说,您使用训练后的模型来做出有用的预测。
(6)回归与分类
  • 回归模型可预测连续值。
    例如,回归模型做出的预测可回答如下问题:
    加利福尼亚州一栋房产的价值是多少?
    用户点击此广告的概率是多少?
  • 分类模型可预测离散值。
    例如,分类模型做出的预测可回答如下问题:
    某个指定电子邮件是垃圾邮件还是非垃圾邮件?
    这是一张狗、猫还是仓鼠图片?

2018年11月20日星期二

MAC_049:安装 Shadowsock

参考文献:
1. https://shadowsocks.org/en/download/clients.html

MAC_048:安装 Python

环境:MAC OS X 10.14.1 (18B75)

1. 安装 Python2
MAC 本身自带 Python 2
(1)检查 Python 版本

$ python --version
 Python 2.7.10
(2)安装 pip
pip 是 python 包管理工具。

$ sudo easy_install pip
(3)查看 pip 版本

$ pip --version
pip 18.1 from /Library/Python/2.7/site-packages/pip-18.1-py2.7.egg/pip (python 2.7)
(4)卸载 pip

$ sudo pip uninstall pip

2. 安装 Python3
macOS(Mac OS X)可用 Homebrew 安装 Python 3,再用通过 get-pip.py 安装 pip
(1)安装 Homebrew
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
(2)安装 Python3
$ brew install python3
(3)查看 Python3 版本
$ python3 --version
Python 3.7.1 (default, Nov  6 2018, 18:46:03)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(4)安装 pip3
$ curl https://bootstrap.pypa.io/get-pip.py | python3
(5)查看 pip3 版本
$ pip3 --version
pip 18.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)

参考文献
1. https://brew.sh/index_zh-cn.html

2018年11月17日星期六

Human_020:《人类简史》读书笔记之二十:智人末日


第二十章 智人末日

40亿年的时间里,地球上每一种生物的演化都是依循着自然选择的法则,智人也不例外。
虽然在自然选择方面,智人远远超出了其他生物,取得了很大成就,但还是没有打破生物因素的限制。

直到21世纪,自然选择的法则开始被打破,科学家们开始改造各种生物。
举例:荧光绿的兔子。

这可能会成为地球出现以来最重要的生物学革命,因为它将彻底改变生命的游戏规则。
有三种方式让智慧设计取代自然选择:生物工程、仿生工程、无机生命工程。

生物工程:指人类刻意在生物层次进行的干预行为,目的在于改变生物体的外形、能力、需求或欲望。
举例:背上长一只耳朵的老鼠。耐寒的马铃薯。奶牛的乳腺炎问题。
基因工程引发了一系列伦理、政治和意识形态的问题。
复活长毛象 -> 复活尼安德特人 -> 创造更完美的智人 -> 超人类

仿生工程:结合有机和无机组织,创造出“生化人”。
从某种意义上,现代人几乎多多少少都是生化人,用其他设备来辅助我们的感官和能力。
举例:仿生苍蝇或蟑螂。仿生鲨鱼。生化耳,生化视网膜,生化手臂。
最革命性的仿生就是“脑机接口”,几个大脑彼此互联,形成“脑际网络”。
侯宝林的相声说的,你也做,我也做,大伙都能做,不能一块做。有了“脑际网络”,大家就可以一块做梦啦。

无机生命工程:能够独立演化的计算机程序和病毒。
基因遗传演化程序,程序一旦发动之后,就会开始自由演化,谁都不能掌握它的发展方向。

假设把你的大脑整个备份到硬盘上,再用笔记本来读取运作。这样一来,笔记本是不是就能像智人一样思考和感受呢?
如果是的话,那算是你吗?还是算别人呢?
如果建立起一个个数字心灵,完全由计算机程序组成,拥有我的知觉、意识和记忆,这又算是什么?
如果删除了这个程序,算是谋杀吗?

我们现在这个时代,是有史以来第一次认为所有人类应该享有基本上的平等。
然而,我们可能正准备要打造一个最不平等的社会,因为根据DNA为人量身定做的个人话医学时代已经到来。

未来科技的真正潜力并不在于改变什么车辆和武器,而是改变智人本身,包括我们的情感和欲望。
未来世界的主人和我们之间的差异,大于智人和尼安德特人的差异,未来的主人翁可能接近神的概念。
这种超人类看我们,就好像我们看尼安德特人一样,带着轻蔑和不屑。

人类很难接受的一个事实就是,科学不仅能够改造身体,也能改造心灵。
人类希望自己永远是万物之灵,再也不会有比人类更优秀的物种。

如果智人的历史终将谢幕,我们将是第一代超人类的设计者,他们与我们有着完全不同的意识层次。
作为最后一代智人,我们应该问自己最后一个问题:我们究竟想要变成什么?

Human_019:《人类简史》读书笔记之十九:从此过着幸福快乐的日子


第十九章 从此过着幸福快乐的日子

过去的500年间,我们见证了一连串令人惊叹的革命。但是,我们真的更快乐了吗?
把脚印留在月球上的阿姆斯特朗和3万年前把手印留在雪维洞穴的无名氏,二者谁更快乐?
如果后来的人并没有比以前的人快乐,我们又为什么要发展农业、城市、文字、钱币、帝国、科学和工业呢?
历史学家很少问这样的问题,他们记录的都是伟大的思想家、英勇的战士、慈悲的圣人、充满创造力和想象力的艺术家。
但对于一切究竟究竟怎么给个人带来快乐和痛苦,却只字未提。
说的真好,快乐才是最重要的历史问题啊。

历史一再证实,人类有了权力或者能力就可能滥用,所以要说能力越高就越幸福,看来实在有些天真。
相反,认为人类的种种新发明,只是让我们与伊甸园离的更远了,这种说法也流于武断。

虽然智人取得了空前的成就,但代价就是赔上了几乎所有其他动物的命运。
过去两个世纪间,有数百亿动物遭到现代工业制度的剥削,其冷酷程度在整个地球史上前所未有。
所以要评估全球幸福程度的时候,只看人类是否幸福是有失公允的。

快乐如何计算和测量?
一般来说,快乐是“主观感到幸福”。
如果快乐是主观的,这是不是自由主义的一个观点而已。
因为我们不断被各种媒体洗脑:“忠于自我”、“倾听你自己”、“顺从你的渴望”。
依照这个观点,快乐是种个人内在的感受。如果快乐是内在感受,那怎样由外部测量呢?

社会学家通过发调查问卷,再将结果与财富和政治自由等社会经济因素结合。
通过调查,目前的结论是:
1)金钱的确会带来快乐,但有一定限度,超过限度之后的效果就不明显。
2)疾病会短期降低人的幸福感,但除非病情不断恶化,否则疾病并不会造成长期的不快。
3)家庭和社群要比金钱和健康更能影响快乐。
举例: 双胞胎的不同遭遇,一个车祸,终身残疾;一个中了乐透大奖。
有这种可能性,虽然过去两个世纪人类在物质条件上有了很大改善,但因为家庭崩溃、社会失调,所以两者的作用相互抵消了。

自由也不一定让我们快乐,虽然我们可以自由选择对方,但对方也可以选择离开我们。
哈,别说,还真的是这样啊。

快乐并不在于任何像是财富、健康、甚至社群之类的客观条,而在于客观条件和主观期望之间是否相符。
但这一切不就是老生常谈吗?千年之前的先知早就说过,重要的是知足。

如果快乐由期望而定,铺天盖地的广告正在不知不觉地让全球越来越不开心。
有没有可能,第三世界国家之所以会对生活不满,不只是因为贫穷、疾病、腐败和政治压迫,也是因为他们看到了第一世界国家的生活标准?
举例:埃及总统穆巴拉克被推翻。

快乐的化学成分
生物学家认为,人类的心理和情感世界是数百万年演化的结果。所有的心理状态,是由神经和各种化学物质构成的复杂系统决定的。
所以升官发财中乐透,找到真正的爱情,并不是真正让我们快乐的原因。我们快乐的唯一原因,是身体内发出快感的感官感受。
人类演化的结果,就是不会太快乐,也不会太痛苦。

比如,如果性交没有高潮,男性就不会那么热衷。但如果高潮永续不退,男性会一直开心下去,就没有做其他事的动力。
这个举例真是辩证啊,居然由此可以反推出来人类不会太快乐,也不会太痛苦。

人类的快乐生化机制就像是个恒温空调系统,但每个人的恒温设定值是不一样的。
当我们的期望得到满足时,虽然有短暂的刺激,但很快就会回到原点。
想想你周围的朋友,是不是有些人,不论发生多糟的事,还能保持愉快?
是不是也有人,不管得到了多大的恩赐,还是一直郁郁寡欢?

已婚的人比单身的人更快乐,但这不一定是因为婚姻带来了快乐,也有可能是快乐带来了婚姻。

如果快乐由生化机制决定,历史这个学科的重要性就要大减。
如果快乐由生化机制决定,又何必又这么多的混乱、恐惧、流血和战争?
如果快乐由生化机制决定,我们只需全力研究如何操纵人类的生化机制。
但是生理上的快感不等于生命有意义的快乐。
那生命的意义又是什么呢?

从老子到苏格拉底,先哲们不断告诫人们:“认识你自己!”
2500年多年来,只有佛教有系统地研究了快乐的本质和成因。
佛教认为,快乐既不是主观感受到愉悦,也不是主观觉得生命有意义,反而是在于放下追求主观感受这件事。
放下追求主观感受?从未有过的观点,真了不起!

人类太看重自己的感受,希望快乐,避免难过。事实是,人类的主观感受没有任何实质或意义。
主观感受只是一种电光石火的波动,不论你是否感到快乐或难过,觉得生命是否有意义,这都只是一瞬间的波动而已。
“一切有为法,如梦幻泡影,如露亦如电,应作如是观。”

苦的根源既不在于感到悲伤或疼痛,也不在于感觉一切没有意义。“苦”真正的根源在于“追求”主观感受这件事。
“应无所住,而生其心。”

人如果明白自己的所有主观感受都只是一瞬间的波动,不再追求某种感受。
如此一来,虽然感受痛苦,但不再感到悲惨;虽然愉悦,但依旧保持平静,不受干扰。于是,心灵变得澄明自在。

佛教比其他观点深刻的地方在于,快乐不在于我们的主观感受。如果我们越强调主观感受,反而就越感到苦。
如果真是如此,我们过去对于快乐这件事的历史认知就是错的。欲望是否得到满足,是否感受到快乐都不是重点,真正重要的问题在于人是否了解自己。

我们有什么证据,证明今天的人比起远古的采集者或中世纪的农民更快乐呢?

Human_018:《人类简史》读书笔记之十八:一场永远的革命


第十八章 一场永远的革命

人类总重量 3 亿吨,家禽总重量 7 亿吨,其他野生动物 1 亿吨。
由此可见,人类真的已经称霸全球。

人类能用的资源在不断增加,所以那些关于资源短缺的末日预言不会成真。但生态环境的恶化却是铁证如山,不容否认。
或许,6500万年后,会有一群高智商的老鼠心怀感激地回顾人类造成的这场灾难,就像我们现在感谢那颗杀死恐龙的陨石一般。

工业革命以后,人类越来越受到现代产业和政府的支配。
其中最重要的例子就是将过去传统农业社会的时间节奏替换成工业社会一致而且精确的时间概念。
工业革命不仅为人类带来了时刻表和生产线的概念,更将这些概念推广到几乎所有的人类活动当中。
1880年,英国立法规定全英国的时刻表都必须以格林尼治时间为准。这是史上第一次有国家采取了全国统一的时刻表,要求人民依据人工的时钟来过生活,而不是依据当地的日升日落周期有所调整。
天才的德国物理学家,找出一套方法,只要根据大本钟钟声在广播直播中的微小差异,就能判断伦敦当时的天气。

除了采用工业化的时间,工业革命还对都市化、农民阶级消失、工业无产阶级的兴起、对普罗大众的赋能、民主化、青少年文化,以及父权社会的解体。
然后这一切,都比不上有史以来人类最大的社会革命:家庭和地方社群崩溃,改由国家和市场取代。

国家、市场与个人之间的交易并不容易。国家和市场对于权利义务的划分意见不同,个人抱怨这两者要的太多,给的太少。
很多时候,个人遭到市场的剥削,而国家不但不保护个人,反而动用军队、警察和官僚施加迫害。
经过数百万年的演化,人类的生活和思考方式都预设自己属于社群。但仅仅过了两个世纪,我们就成了互相疏远的个人。这可以说是文化力量的最佳证明。

在现代晚期,虽然暴力和恐惧达到前所未有的高峰,但和平与安宁也同时来到历史新高。“这是最好的年代,这是最坏的年代”。

1945年以来,多数帝国都选择了和平的退位方式,帝国解体崩溃的过程迅速、平和且有序。
今天世上任何地方都已经不可能在发生像是古罗马人、蒙古人和土耳其人那些逐鹿天下的事。
然而,在 1945年以前,国际政治的铁律是说,“任何两个相邻的政体,都有某种可能,在一年之内向对方宣战”。

为何现在是四方和平、战争难以爆发呢?
首先是战争成本的大幅上升,最高诺贝尔和平奖应该颁给奥本海默。
其次是战争的利润下降,今天的财富形式是人力资本、科技知识、复杂的社会经济结构,这个无法靠掠夺和占领。
同时,和平成为了一笔越来越划算的生意。
最后,全球的政治文化也有了结构性的大变动,爱好和平的精英分子领导了这个时代。

大多数国家之所以不再发动全面战争的原因很简单,就是因为他们已经不再能够完全独立行事。
任何经济或外交政策都不可能自外于他国,全面性的战争也不可能独自发动。
我们正在面临全球帝国的形成,全球帝国的疆域就是全世界,所以世界和平也就能够得到有效的维持。

Human_017:《人类简史》读书笔记之十七:工业的巨轮


第十七章 工业的巨轮

工业革命的核心,其实就是能源转换的革命。
我们能够使用的能源其实无穷无尽,唯一的限制只在于我们的无知。

工业革命最要要的一点,是第二次农业革命。

机械化不只是机器的事,连植物和动物也同样遭到机械化。
各种农场上的动物不再被视为活生生的、能够感受到痛苦的生物,而是被视为机器一般对待。
举例:蛋鸡、猪、奶牛的悲惨一生。
但讽刺的是,正式那些知道了挤奶器和集蛋器的科学,指出这些哺乳动物也有复杂的感觉和情绪。
它们不仅能够感受生理上的痛苦,也同样能够感受到心理上的痛苦。
举例:哈利的猴子实验,太让人难受了。

大西洋奴隶贸易并非出于对非洲人的仇恨,现代畜牧业也不是出于对动物的仇恨。一切还是因为利益。

随着农业工业化,只需要越来越少的农民,就足以养活越来越多的人口。
正因为农业释放了数十亿的人力,人类才开始创造出了各种产品。
人类有史以来第一次,生产超出了需求。那么问题来了: 谁要来买这些产品?

资本主义和消费主义和一枚硬币的正反两面。

Human_016:《人类简史》读书笔记之十六:资本主义教条


第十六章 资本主义教条

现代经济的成长源于信用,信用就是“今天的蛋糕”和“明天的蛋糕”之间的差价。

资本主义:生产的利润,必须再投资于提高产量。
资本主义认为经济可以无穷无尽地发展下去,这和我们的日常认识背道而驰。
人类的经济在整个现代时期之所以能够不可思议地持续呈指数增长,唯一的原因,就是科学家总是能每隔几年就取得另一项发现。

清朝由征服者建立政权,主要靠税收和掠夺(两者的差异其实很细微)取得资金,很少需要用到信贷,更不用提是否关心投资者的利益。
欧洲则是开始由商人和银行家成为统治精英,资金来源从税收改为信贷,一切的目标就是要让投资者取得最高的报酬。
毕竟,没人喜欢交税,但人人都乐于投资。

西方政府几乎就像是资本家的工会。
举例:中英第一次鸦片战争

大西洋奴隶贸易兴起的原因,并不是欧洲人对非洲人有什么种族仇恨。而是因为那 6%的贩奴利益。

经济大饼真的能够无限制变大吗?每块饼都需要原材料和能源。所有资源迟早会被耗尽,这将在什么时候?