首页 > 杂记 > 正文

分层的概念——认知的基石


今年以来,一直在自学一些新的技术——准确地说——是自己以前没怎么深入接触过的技术。对于具体的技术,我越来越强烈的感受是,如果不能系统性地讲解出来,只是分享一些零散的知识点,那么它的价值将是大打折扣的。因此,一些具体的知识,等时机成熟了我们再来讨论。然而,有一些知识和方法,它们往往比具体知识有着更普遍的意义,而且对每个人来说都是值得探讨的课题,这就是有关「学习」本身的知识,以及对认知过程本身的分析。所以,最近我打算重新谈谈学习的方法,以及认知过程中涉及到的一些基本问题。

我们的认知是建立在「概念」的基础上的,在此基础上「概念」之间形成「关系」,就构成了一个知识体系——一个知识的大网。而对于「概念」和它们之间的「关系」的把握,我们靠的是什么呢?没错,靠的是「逻辑」。

如果我们从认知的角度出发,重点来关注概念之间的「依赖」关系,那么可以看到概念之间会呈现出清晰的分层结构。为了理解一个系统或者新的知识领域,我们必须要找到这样一个概念的层次,然后把我们所有的认知构建在这个层次之上。为了把这个观点说得更清楚,接下来我举几个例子。

分层的概念举例

  • 客户端的UI编程。一般情况下,我们对于UI编程的认知是建立在与「View」相关的各个概念之上的。View是什么呢?可以认为它是一块矩形的区域,具有位置和大小,能够渲染出内容,可以和用户交互。只要理解了View这一层次,我们就知道如何用它来组装出各种页面,我们平常大部分的客户端编程工作便能胜任了。单以渲染绘制方面来说,我们可以自定义绘制的内容,可以绘制出矩形、圆形,也可以绘制出任意图片。但如果再往下深入到「OpenGL ES」的概念层次,那么就能理解所有形状其实都由三角形组成的(包括圆形也是三角形拼成的),一个形状和纹理是怎么绘制出来的,少数的几个顶点是怎么对应到那么多个像素点上去的,这中间是怎样的一种插值的过程。如果我们想实现一些稍微复杂的功能,比如滤镜(需要执行像素级的操作),就很有可能需要在「OpenGL ES」这个层面上去理解各个概念。
  • 网络通信。以HTTP通信为例,如果我们的认识只是局限在一些具体的API层次,不管它是系统本身提供的(比如Java的HttpURLConnection,或者iOS的NSURLConnection),还是第三方库提供的(比如HttpClient, OkHttp, Android-Volley等),那么我们虽然也有可能在某些情况下写出能运行的代码,但是使用不同编程环境的人之间(比如前后端之间)就基本没法交流,因为具体的API都有所差异。为了能互相听懂对方的话,我们必须要到HTTP协议的层次去交谈,这样,我们才能分清什么是Method,什么是参数,什么是Header,什么是Body,各部分的格式是怎样的,缓存策略以及Cookie这些概念和HTTP Header有什么关系,等等。更进一步,如果我们涉及到要做一些更底层的工作,比如网络性能优化,或者协议处理,我们就必须理解到TCP这一层,理解HTTP和TCP是怎样的关系,一个HTTP请求或响应怎样被封装进TCP包,多个HTTP请求如何共用一条TCP连接,等等这些问题。
  • Web框架。后端在实现一个Web Server的时候,经常基于一些成熟的MVC框架,就类似Spring这样的。这种MVC框架所提供的功能,就组成了一个概念层次。我们知道,在这个层次上,我们只需要写一个小小的Java Annotation,就能够按照我们精确定义的规则来实现Request Mapping;我们在处理方法最后返回的Java对象,也会被转成正确的格式(一般是JSON)输出到网络上去;配置一个拦截器就能够进行某种通用的处理。但是,有时候我们也需要深入到下一个层次,Servlet规范的层次,去了解框架的所有这些功能是怎样在Servlet的基础上构建出来的。只有这样,当我们碰到一些难解的疑难杂症,或者想对框架进行扩展的时候,才知道从哪里下手。
  • 响应式编程。当我们使用Rx系列的架构来构建我们的代码时,在这一层次上,我们可以执行各种变换,灵活地切换线程,流式地处理事件和逻辑。但如果要理解这背后到底发生了什么,我们必须要深入一层,把这些抽象的东西构建在我们已经熟知的基本概念之上,这些基本概念包括:多线程、同步原语、事件队列,以及一些基本的异步逻辑。
  • 机器学习。利用机器学习,我们至少能解决两个大类的问题:分类(Classification)和回归(Regression)。为了解决这些问题,机器学习就像一个工具箱,为我们提供了很多现成的的算法框架,比如:LR, 决策树,随机森林,Gradient boosting等等,还有近两年大热的深度学习的各种算法,但要想做到深入的话呢,只是会使用这些现成的算法库还不够,还需要在底层的数学原理上有所把握。比如,研究优化理论,才能够有更好的思路去设计和优化目标函数;研究统计学,才能够理解机器学习本质的由来,理解为什么机器学习的方法能够使得模型一步步地逼近真实的数据分布;研究线性代数,才能够更灵活地使用矩阵这一数学工具,提高了性能且表达简洁,才能够更好地理解机器学习中涉及到的维数灾难及降维问题;研究信息论,才能够准确地度量不同概率分布之间的差异。
  • 计算机图形学。如果要做2D的图像处理,基本上能理解OpenGL的规范和Shader程序的语法就差不多能进行开发工作了。但如果要做3D的图像处理,则需要理解3D的坐标变换(model, view, projection),尤其是投影变换。这个层次的知识超出了OpenGL的范畴,本质上是线性代数中的线性变换。

例子就先到此打住,因为具体的例子永远也举不完。为什么在认知的过程中,我们要把概念清晰地分层呢?

  1. 必要性。有些时候,当我们接触一个新的知识领域时,有大量的概念需要理解。如果我们想理解上层的概念,那么必须先理解下层的概念,一层层推下去,直到我们到达了一层我们已经熟知的概念为止。这时候,我们才能理解整个体系。
  2. 更深刻的理解。几乎所有情况下,理解了下层的概念,上层的概念也变得更加清晰。试想一下,分别站在四个不同的层面来看系统:JDK,JVM,POSIX规范,Kernel,看到的东西自然相差甚远。很多情况下,受限于时间和精力,我们没有机会去一窥各个层次的究竟,但我们也不应该忘了在研究方法中这种层层深入的可能性。
  3. 更清晰的知识结构。如果我们头脑中对于某个知识领域的认识,只有一系列杂乱的概念,且它们来自各个不同的层次,那么我们的知识就是支离破碎的。在这样一个支离破碎的基础上,我们是没法进行正常的逻辑思考的。

概念的层次与Spec的关系

如果你不是第一天读我写的东西,那么很可能你已经知道了Spec这个概念代表的是啥。这个概念是我在《技术的正宗与野路子》中提出来的,它是对于某项技术或知识的一份完备的、系统性的描述,我们学习的目的,可以说就是能读懂它,理解它。

从我们今天所讨论的话题来讲,一个Spec其实定义了某个层次的所有概念,而且这份定义是比较完备的。之所以说是完备的,是因为只要我们理解了一份Spec,那么对应的那个层次的认知过程就基本结束了,我们可以在这个层次上构建起必需的逻辑,并有能力解决从属于那个层次的一切问题。所以说,重新归纳Spec的定义,我们可以加上一个定语:它是对于某项技术或知识的某个层次的一份完备的、系统性的描述。

通俗地说,某个特定层次的概念,就相当于那个层次的一门交流的语言。就好比说,程序员对设计师或产品经理应该使用产品语言来交流,但如果我们非要使用「数据存储的表结构」、「接口返回的数据格式」之类的词汇,交流就变得无法进行。因为这不是双方都能理解的语言。而我们在思考的时候,在头脑中也是需要一种「逻辑语言」来进行描述的,这门「逻辑语言」就是某个概念层次,也或者是一份完备的Spec。

自底向上还是自顶向下

像计算机科学中的很多问题一样,认知的过程也可以分为自底向上(bottom-up)或自顶向下(top-down)的。

就比如要理解前面提到的降维问题,我们需要去学习一些降维的技术,比如很有名的PCA。如果按照自顶向下的方法去理解这个概念,那么,要理解PCA,就需要先理解特征值和特征向量;要理解特征值和特征向量,就需要先理解线性变换;要理解线性变换,就需要先理解线性映射;要理解线性映射,就需要先理解线性空间;要理解理解线性空间,就需要先理解向量、向量组、线性相关和线性无关等诸多的基础概念。

上述自顶向下的理解过程,与我们学校里传统的教学过程正好相反。两种方法各有优缺点。

  • 自底向上,知识的根基会打得更深。但缺点是费时费力,目的性不明确。
  • 自顶向下,优点是目的性强。缺点是获得的知识可能不够系统。

在实际中,两种方法应该互为补充。当时间相对紧迫,而所面临的新领域又相对庞大的时候,这时候可以选用自顶向下的方法,来迅速地自上而下地穿透各个概念层次,把遭遇到的陌生的概念变成可以理解的概念。而当时间充裕之后,对于重要的知识领域,就可以采用自底向上的方式,重新把各个概念层次自下而上地梳理一遍,以达到一个更系统性的理解。


对于具体知识的逐层深入地认知,是永远没有止境的。而学习和认知的方法本身也是一种技术,它们就蕴藏在那些具体问题之间。只要你终日和它们打交道,你就一定会摸索出其中的一些门道,并逐渐变得精于此道。

(完)

其它精选文章


原创文章,转载请注明出处,并包含下面的二维码!否则拒绝转载!
本文链接:http://zhangtielei.com/posts/blog-concept-hierarchy.html
我的微信公众号: tielei-blog (张铁蕾)
上篇: 朋友,你的时间够用吗?