IT技术精华 http://it.taocms.org/ 聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长 2020-12-03 IT技术精华 50384 特征工程数据预处理之抽样 在分析数据或进行算法模型训练前有时需要先对数据进行抽样,这里整理了抽样的一些知识点。

什么情况下需要会用到抽样?

  • 数据量太大,计算能力不足。
  • 抽样调查,小部分数据即可反应全局情况。
  • 时效要求,通过抽样快速实现概念验证。
  • 定性分析的工作需要。
  • 无法实现全覆盖的场景,比如满意度调查等。
  • 解决样本不均衡问题。

常用数据抽样的方法

随机抽样(用的最多)

该抽样方法是按等概率原则直接从总中抽取n个样本,这种随机样本方法简单,易于操作;但是它并不能保证样本能完美的代表总体,这种抽样的基本前提是所有样本个体都是等概率分布,但真实情况却是很多数样本都不是或无法判断是否等概率分布。在简单随机抽样中,得到的结果是不重复的样本集,还可以使用有放回的简单随机抽样,这样得到的样本集中会存在重复数据。该方法适用于个体分布均匀的场景。

使用Pandas进行数据随机

DataFrame.sample(n=None, frac=None, replace=False, weights=None, random_state=None, axis=None)

参数说明:

  • n:需要抽样的数量
  • frac:需要抽样的比例,0-1之前的小数。n和frac只能传一个
  • replace:是否允许同一行被抽到多次,默认为False
  • weights:默认为等概率加权
  • random_state:随机数发生器种子
  • axis:取值为0时,抽取行。取值为1时,抽取列。

等距抽样

等距抽样是先将总体的每个个体按顺序编号,然后再计算出抽样间隔,再按照固定抽样间隔抽取个体。这种操作方法易于理解、简便易行,但当总体样本的分布呈现明显的分布规律时容易产生偏差,例如增减趋势、周期性规律等。该方法适用于个体分布均匀或呈现明显的均匀分布规律,无明显趋势或周期性规律的数据。

比如当需要对用户访问页面进行分析时,由于一个用户存在多个浏览记录,如果采用随机抽样可能会导致抽取到的用户访问页面不全的信息。改进方案为按照会员维度等距进行抽样。比如按会员号尾号进行抽样。

分层抽样

分层抽样是先将所有个体样本按照某种特征划分为几个类别,然后从每个类别中使用随机抽样或等距抽样的方法选择个体组成样本。这种操作方法能明显的降低抽样误差,并且便于针对不同类别的数据样本进行单独研究,因此是一种较好的实现方法。该方法适用于带有分类逻辑的属性、标签等特征的数据。

通过分层抽样解决样本不均衡的问题:

  • 过抽样:增加分类中少数类样本的数量,最简单的方法就是复制少数类样本形成多条记录。改进的过抽样方法是在少数类中加入随机噪声、干扰数据或通过一定规则产生新的合成样本。例如SMOTE算法。过抽样方法应用极广泛。
  • 欠抽样:减少分类中多数类样本的数量,最简单的方法就是随机去掉一些多数样本。

通过正负样本的惩罚权重来解决不均衡:对于分类中不同样本数量的类别分别赋予不同权重(一般小样本量类别权重大,大样本量类别权重小),然后进行计算和建模,例SVM。

机器学习需要多大的数据量?

很多机器学习研究者普遍存在的问题:“我的项目需要多少训练数据?”这个问题经常让不少开发者非常苦恼。实际上,这个问题回答起来比看上去要复杂的多,因为其中要涉及到很多因素,比如你使用的模型类型,模型在实际业务中的用途等等。

为何很难预估你的数据集大小?

确定目标数据量的许多困难源于训练过程的目标。有很多文章都讲到了模型训练过程的工作原理,但要记住,我们训练的目的是构建一个能理解数据背后模式和关系的模型,而不仅仅是理解数据本身。收集数据时,我们需要确保所拥有的数据数量能教会算法数据中的关系,以及数据和结果之间的关系。

这看起来很容易预测。但实际上机器学习项目的目标多种多样,可以产生大量的训练数据类型。因此,每个项目都有各自独特的多种因素,这使得提前计算数据需求变得非常困难,可能包括以下部分或全部内容:

  • 模型的复杂度:模型为完成任务而必须考虑的每个参数都会增加训练所需的数据量。例如,用于识别特定汽车品牌的模型具有少量的参数,主要与车辆的形状相关。而如果是一个必须从长远考虑汽车成本的模型,不仅要顾及到包括汽车品牌和状况的因素,还有经济和社会因素。由于这种复杂程度更高,第二个模型需要比第一个模型更多的数据。
  • 训练方法:由于模型必须理解更多的互连参数,由此导致的复杂性使得训练模型的方式也被迫发生改变。传统的机器学习算法使用结构化学习,这意味着它们很快就会出现附加数据投入产出比很低的情况。相反,深度学习模型可以找出自己的参数,并学习如何在没有结构的情况下进行改进。这意味着它们不仅需要更多的数据,而且还有更长的学习曲线,其中进一步增加数据具有积极的影响。因此,你使用的训练方法会导致对你的模型有用的训练数据量发生显著变化。
  • 标签需求:根据实际任务,我们会以不同方式注释数据点。这可能会导致数据生成的标签数量,以及创建这些标签所需的工作量发生明显变化。例如,如果我们有1000个输入数据句子用于情感分析,我们可能只需要将它们标记为正面情感或负面情感,因此每个句子产生一个标签。但是,如果对同样1000个句子进行注释用于实体提取,则可能需要为每个句子标记5个单词。尽管具有相同的原始输入数据,但是一个任务产生的标签比另一个任务多五倍。因此,我们准备数据的方式可能会影响项目所需的工作量和花费的成本。
  • 误差容忍度:模型在你的实际业务中的预期作用也会影响数据量。对于预测天气的模型,20%的误差率是可接受的,但如果用于检测有心脏病发作风险的患者,这个误差率万万不可。在边界案例方面的改进可以降低这种风险。如果你的算法具有高度风险规避或者对于项目能否成功至关重要,那么你需要的数据量会大幅增加,才能追求完美性能。
  • 输入的多样性:我们生活在一个复杂的世界中,可以向模型输入各种类型的输入数据。例如,聊天机器人必须能够理解各种语言,哪怕它们是用一系列正式,非正式甚至语法不正确的风格写就的。如果无法高度控制模型的输入,就需要更多数据来帮助你的模型在不可预测的环境中运行。

从上面这些因素可以清楚地看到,我们需要的数据量取决于项目的独特需求和预期目标。最后,项目负责人必须自己平衡这些因素,并提出自己的目标。那么有没有什么方法呢?

有,接下来就看看有哪些方法可以帮我们在刚开始时确定所需的数据量。

我们该如何计算数据需求?

确定任何给定算法所需的确切数据数量可能无法做到。幸好,基于整体预估的方法能帮你分析项目的训练数据需求量,以下两种方法均可:

  • 10 倍法则:一个常见的经验法则是模型通常需要超出其自由度 10 倍的数据量。这里的自由度可以是影响模型输出的参数,是数据点的一个属性,或者更简单地说,是数据集中的列。10 倍法则的目标就是是抵消这些组合参数为模型输入带来的变化。对于复杂模型而言,这可能没有帮助。但是,这条法则能让我们快速估算数据集的数量,保证项目保持运行。
  • 学习曲线:如果你已经有一些数据并希望根据更多事实情况做出决策,可以考虑创建一个研究示例,根据数据集的大小来评估模型的能力。通过把结果绘制在图表上,你应该能够找出数据集大小与模型能力之间的关系,同时还要确定什么时候添加更多数据的收益会不断递减。这种方法需要更多人力工作,包括创建一些逻辑回归问题,但至少比简单一猜要靠谱得多。

通常,最好在刚开始时用手头已有的数据训练模型,在你认为有必要时再添加更多数据。一旦项目取得一些结果,你对数据的需求会越来越明显。不过,有些朋友还是希望能在项目开水前有个确切的数字,下面是我们在网上找到的项目数据集大小的一些估计。也许这些例子能让你对自己项目所需的数据集大小心里有个数。

数量 VS 质量

数据的局限性也会是模型的局限性。但是,在所有关于你需要多少数据的讨论中,不要忘记这条也适用于数据质量和数量。对于模型来说,一百万个混乱的数据反而不如 100 个干净数据,后者实际上更有助于算法达成目标。无论你正在搭建什么样的模型,都要确保正在使用的数据能为你提供坚实的基础和最佳的成功机会。

看了上面的介绍,感觉还是云里雾里的,个人整理的大致方案:(没有什么理论支撑)

  • 大于 (数值型特征数量+每个类别型特征的类别数) * 10
  • 小于 100万

个人处理时,也没有考虑那么多内容,通常取100万数据进行作为训练集。

参考链接:


立即购买:淘宝网]]>
IT技术精华 2020-12-01 22:12:04
50383 JupyterLab HIVE数据同步流程 公司的数据存放在HDFS上,但是模型的训练时需要用到这部分数据,于是就有了数据同步的需求。以下是个人整理的数据同步流程,仅适用于公司内部,其他地方由于环境不同可能不可用。

数据从Hive同步到JupyterLab

通过Hive查看数据文件位置

可以通过Hive的show create table(建表语句)查看数据库表的路径:

show create table tmp_db.my_table_name

执行后可查询到数据文件路径,例如:

  'viewfs://dcfs/user/hive/warehouse/tmp_db/my_table_name'

实际对应的位置则为:/user/hive/warehouse/tmp_db/my_table_name

通过HDFS 命令同步数据到JupyterLab

在Jupyterlab中打开Terminal然后输入如下命名(路径为上一步Hive查询到的路径):

hdfs dfs -copyToLocal /user/hive/warehouse/tmp_db/my_table_name ./

hdfs dfs命令详解

查看文件常用命令

  • hdfs dfs -ls path 查看文件列表
  • hdfs dfs -lsr path 递归查看文件列表
  • hdfs dfs -du path 查看path下的磁盘情况,单位字节

创建文件夹

  • hdfs dfs -mkdir path

注:该命令可递归创建文件夹,不可重复创建,在Linux文件系统中不可见

创建文件

  • hdfs dfs -touchz path

注:该命令不可递归创建文件即当该文件的上级目录不存在时无法创建该文件,可重复创建但会覆盖原有的内容

复制文件和目录

  • hdfs dfs -cp 源目录 目标目录

移动文件和目录

  • hdfs dfs -mv 源目录 目标目录

赋予权限

  • hdfs dfs -chmod [权限参数][拥有者][:[组]] path

上传文件

  • hdfs dfs -put 源文件夹 目标文件夹

类似命令:

  • hdfs dfs -copyFromLocal 源文件夹 目标文件夹 作用同put
  • hdfs dfs -moveFromLocal 源文件夹 目标文件夹 上传后删除本地

下载文件

  • hdfs dfs -get源文件夹 目标文件夹

类似命令

  • hdfs dfs -copyToLocal 源文件夹 目标文件夹 作用同get
  • hdfs dfs -moveToLocal 源文件夹 目标文件夹 get后删除源文件

删除文件

  • hdfs dfs -rm 目标文件
  • hdfs dfs -rmr 目标文件 递归删除(慎用)

通过Pandas读取同步的文件

import pandas as pd
import glob

file_list = glob.glob('my_table_name/*')
df_list = []

for file in file_list:
    df_temp = pd.read_csv(file, sep="01", header=None, na_values=['N'])
    df_list.append(df_temp)

df = pd.concat(df_list, ignore_index=True)
df.to_csv('output.csv')

注意:

  • sep为HDFS存储的数据的间隔符号
  • na_values为HDFS文件中NULL值的符号

数据从jupyterlab同步到Hive仓库

通过hdfs命令将Jupyter中的文件写入到HDFS

hdfs dfs -copyFromLocal -f poi_info.csv /user/hive/warehouse/tmp_db/poi_info

通过HIVE创建表并关联上数据

create external table if not exists tmp_hotel.qw_20200928_poi
poi_id bigint,
poi_name string,
    lat string,
    lon string,
)
COMMENT 'poi location'
ROW FORMAT DELIMITED FIELDS TERMINATED BY 't'
LOCATION '/user/hive/warehouse/tmp_db/poi_info';

备注:

  • ROW FORMAT DELIMITED FIELDS TERMINATED BY 需要指名CSV的字段间隔符号。

立即购买:淘宝网]]>
IT技术精华 2020-12-01 22:12:03
50103 解剖学角度的女性 - 读《女人这东西》

一、引言

本书作者 渡边淳一 是日本文学大师,一生发表 130 多部作品。代表作是《失乐园》。渡边淳一的作品多描写两性关系,他本人一度任骨科医生,这本《女人这东西》就是他对于女性的一些见解。

以下我的一些读书感悟。

二、感悟

2.1 男女天生的差异巨大

书中举了很多例子,表明男性和女性天生就有着巨大差异,差异大到可以想像成不同的物种。这种差异也带了很多男女沟通之间的矛盾。比如:

  • 女性投入两性生活会令她表现得更加柔和,心情更加欢愉。而男性却会无精打采,疲惫不堪。

  • 又比如女性通常不会对旧爱藕断丝连,而男性通常对于前女友常保持着总有一天可能会再续前缘的心态。

  • 又比如女性通常不会接受没有爱的性,而男性则会把性看成只是性,没有爱有冲动也可能发生性。

2.2 女性的两种快感

书中还提到了女性的两种快感:阴蒂高潮和阴道高潮。而前者可以通过自慰达到,后者只能通过与男性的交合达到,同时后者带来的快感远大于前者。

作者说这种快感会让女性不断沉迷于性,而男性则不会。因为男性对于性的感受来源于新鲜感,所以只会越来越觉得无趣。

2.3 男女对于婚姻不满的差别

女性对于婚姻中的不满常常是非常具体的事情,而男性则是心理层面上的:“我到底能不能一辈子永远爱这个女人?” 这个事情的核心差异是:女性随着夫妻生活的发展,其性体验是越来越好的;而男性的性体验,在结婚刚开始的时候是最高的,之后就慢慢下降。

最终维系男性家庭观的核心是精神层面上的爱,而不再是肉体上的爱。所以,男性对婚姻的不满,通常都是精神层面上的。

2.4 性感缺失的女人

书中提到对于性的感受低的一类女性,这类女性由于过度神经质、坚持自我主张,而无法在婚姻生活中享受到性快乐。因为性快乐需要女性在心理上达到一种牺牲自我的状态。

现代社会对于女性的地位提升,助长了这一类心理问题,作者把这称作女性的非女性化。

2.5 女性的移情

女性通常有三次移情,刚开始女性会在学校拥有恋人般的同伴朋友。一旦遇上中意的男人,则会全情投入到男人身上。而最后孩子出生,逐渐成长,她又会全情移至孩子身上,将丈夫抛在脑后。

男人通常不会像女性这么移情,即便转移,也无法做到像女性这么果断。这两性的差异核心是来自于荷尔蒙,女性行为的变化其实是来自生理激素的变化。

三、结束语

这本书的很多观点让我感同身受。我们人类虽然非常厉害,站在食物链的顶端,但是我们毕竟也是经过千百万年进化而来的哺乳动物。

男性与女性的很多差异,来源于原始社会的自然选择。比如男性为了更多地将 DNA 遗传,所以性体验最高的是最初的时候,为了性男性有时候不择手段;而女性因为要照顾孩子,留住家庭,所以性体验却越来越高并且对家庭很专一。这背后都是基因的设计。

我们对于身体基因层面的东西无法改变,只能理解和尽可能适应他。

整个人类种群的繁衍,其实是牺牲了个体的体验,来保证整个种群的利益,这也是自然法则之一。


立即购买:淘宝网]]>
IT技术精华 2020-11-30 01:11:06
49807 粒子管理器的 C++ 封装 这篇接着上一篇 粒子系统的设计

TL;DR 在花了一整个晚上用 C++ 完成了这一块的功能后,我陷入了自我怀疑中。到底花这么多精力做这么一小块功能有意义么?强调类型安全无非是为了减少与之关联的代码的缺陷,提高质量;但代码不那么浅显易懂却降低了质量。

我们用 C 实现了一个基于 ECS 结构的粒子系统的管理器,代码 psystem_manager.h 在这里

先来回顾一下设计:在这个粒子系统中,我期望把粒子对象的不同属性分开管理。

即:传统的面向对象的数据结构中,一个对象 particle 可以有很多属性 a,b,c 。通常是用一个结构体(或类)静态定义出来的,这些属性也可以看作是 a b c 组件,它们构成了粒子对象。而在 ECS 结构中,我们在每个时间点,并非去处理一个对象的多个属性,而是处理同一个属性的多个对象。所以,我们最好按属性分类将多个对象的同一属性聚合起来,而不是按对象,把同一对象的不同属性聚合在一起。

这是因为,在处理单个属性时,往往并不关心别的属性。比如,我们在递减生命期,处理生命期结束的对象时,关心的仅仅是生命期这个属性;在处理粒子受到的重力或其它力的影响时,我们只关心当前的加速度和速度;在计算粒子的空间位置时,只关心上一次的位置和瞬间速度;而在渲染时候,无论是生命期、加速度、速度,这些均不关心。

当数据按属性聚合,代码在批量处理数据时,连续内存对 cache 友好,即使属性只有一个字节,也不会因为对齐问题浪费内存。同一属性的数据尺寸完全相同,处理起来更简单。而且粒子对象相互不受影响,我们只是把同一个操作作用在很多组数据上,次序不敏感。非常适合并行处理。

更重要的是,不同类型的粒子需要自由的根据需要组合属性和行为。有的粒子有物理信息参与刚体碰撞运算,有的则只需要显示不需要这个信息;有的粒子有颜色信息,有的不需要有;有的粒子是一个面片,有的却是一个模型,拥有不同的材质。这导致粒子对象包含的信息量是不同的。及时拥有同一属性,作用在上面的行为也可能不同:例如同样是物理形状信息,可能用于刚体碰撞,改变运动轨迹,也可能只是为了触发一下碰撞事件。

在传统的面向对象的方式中,常用多态(C++ 的虚函数)来实现,或者有大量的 if else switch case 。

如果能按组件和行为聚合,那么就能减少大量的分支。每个粒子的功能组合(打开某个特性关闭某个特性)也方便在运行时决定,而不用生成大量的静态类。

但换种方式组织数据也有难点:原本用偏移量和直接的指针聚合在一起的单一对象,被打破分散在了多个数据块中。同一个对象的不同部分建立联系会比较麻烦。这种需求会出现在两个场合:

  1. 对象删除时,需要找到所有的组件删除。
  2. 多个组件有关联处理时,需要从一个组件 A 找到同一个粒子上的组件 B 。

最简单粗暴的方法是还是建一个空的根对象,让它对所有的组件都保留一个指针;然后没有组件都保留一个对这个空对象的指针。在组件比较零碎时,这些相互引用的指针会造成大量的内存占用,甚至超过组件本身的数据尺寸。

我之前写的管理器模块就是实现这样的数据结构,并尽可能的减少额外内存的使用,同时、同一粒子间的的组件相互引用能保持 O(1) 的访问时间。为了让这个管理器的功能内聚,在设计的时候,我考虑了几点:

  1. 管理器只管理关系,不管理数据内存,内存在外部由别的模块负责。
  2. 外部数据被视为可用连续内存块管理,不需要外部记录任何形式的引用值(指针或 id 的形式都不需要)。
  3. 不强制要求外部数据在连续内存块上,由外部自己决定如何储存。
  4. 外部数据推荐用 POD 结构,但不强制。
  5. 可以在迭代的过程中删除对象,不破坏迭代过程。让删除仅仅是做标记,基于粒子系统的特点,即使是刚被删除的对象中的组件,在其上做处理也是无害的。
  6. 整理被删除对象放在统一的地方一次处理,在保持外部内存块连续的基础上,做最少的数据移动。外部数据移动的过程不用 callback 的形式驱动。

我在用 C 语言实现完管理器后,写了一个简单的使用案例 (见 test.c )。同事理解了思路后,接手来做其它部分。他倾向于用 C++ 来完成后面的编写,理由是需要类型安全。

我个人虽然不太喜欢用 C++ 做设计,不过我的工作原则是,在保证模块划分清晰,接口明确的前提下,具体实现尊重实现者的意愿。爱用什么写就用什么写。

做后续的工作的起点,是需要做一个外部的数据容器集,可以存放不同的组件(属性)数据。也就是说,有若干的组件数组,每个放一个特定类型的组件,单个数组里有整个系统内拥有这个属性的所有粒子对象的该组件。

看起来会是这样的:

struct particle_manager *manager;
arrayType1 t1;
arrayType2 t1;
arrayType3 t3;
arrayType4 t4;
...

这就保存了整个粒子系统中的所有数据,而关联关系的数据在之前实现的 particle_manager 结构中。加起来就是全部。

显然,把 Type1 到 TypeN 的 array (实际用 std::vector 实现) 这么平坦的放在这里,会造成大量的重复代码。因为外部要处理 manager 模块的 arrange remap 信息(在删除对象后数据重排列)时,处理代码并不关心具体类型。所以很自然的,数据结构就演变为:

attribute * attribs[MAXCOMPONENTS];
struct particle_manager *manager;

给这些不同类型的数据容器加上一个基类 attribute ,保存一个基类指针的数据就够了。这样方便处理。

我们在实现这个系统(给关联关系的管理器增加实际数据的管理)时,需求有三:

  1. 添加一个由若干组件构成的粒子:需要分别给不同的属性容器追加数据,然后告诉管理器增加一个粒子。
  2. 删除任何一个组件时,同时删除和这个组件关联的组件(同一粒子对象)。管理器会报告数据块如何重新排列,接下来需要按这个信息重组数据块。
  3. 迭代特定的组件数组。依次取出数组中的每个组件数据。

对于需求 3 ,由于实际上我们就是用平坦内存(std::vector)储存的,迭代本身是非常廉价的。但是以上结构直接使用是类型不安全的。C++ 通过模板技术可以实现类型安全并不增加额外的成本。这是实现者选用 C++ 封装的动机。

我们可以提供这样一个 api :

vector<类型>& particle_system::attrib<类型>();

使用者正确的填写了组件的类型,就能返回对应的容器引用。它干的事情其实只是通过类型 ID 找到 attribs[] 中对应的那个 attrib * ,取出里面的引用。这个功能非常简单,C++ 带来的好处就是强类型避免犯错。如果类型没有写对,不能通过编译,而不是发生运行时错误(如果是 C 接口,恐怕就是使用类型 ID 而不是类型本身,也只能报告运行时错误)。

另外,还有一些衍生的需求。属性数据类型可以被分为三类:

  1. POD 的数据结构。
  2. 原生数据类型(例如 float int 这些)或其它库提供的基础数据结构(例如 float3 matrix 等)。
  3. 复杂的接口(带虚表的对象)。

对于 3 ,希望用 raw 指针而不是智能指针(智能指针其实就是 2 )。这是因为依然希望最终在处理整块数据的数据,面对的是连续内存块上的有明确数据布局的数据。raw 指针会比智能指针对象更清晰。

但这给 C++ 封装带来了一点挑战。因为容器可以是 container<类型> 也可以是 container<类型*> 。再重排列的算法上也有一点差别:raw 指针需要更小心的处理。这会用到模板的偏特化来解决。

另外,还需要从一个组件找到关联的组件数据。我希望无论是容器里是值对象还是 raw 指针,接口全部返回组件的指针,并用 nullptr 表示没有关联的组件。不同的数据储存形式也加大了封装层的复杂度。

同事在实现的时候遇到了一些语言技术细节上的困难(毕竟我们并不常用 C++ 开发),我们一起讨论,这激起了我的一点兴趣。我这些年都没有实际用 C++ 创作过新代码(但一直有阅读和维护第三方 C++ 项目,并不算陌生),虽然我对 C++ 新标准不算熟悉,但还是勉强能写写。这次的需求本质上并不复杂,只是用 C++ 模板封装一个比较简单的功能(给粒子管理器在关系管理的基础上增加数据管理),并提供类型安全。我决定自己也试着实现一个版本。

我个人的预期是这样的:

  1. 少用不必要的复杂模式,能解决问题就够了。目的就是达到类型安全,在使用的时候不再写显式的类型转换。写错类型时,在编译器就能检查出来。
  2. 头文件只暴露最少的信息,不在头文件中暴露模板方法的实现,尽量不在头文件中引用标准库或及其库,避免编译时间的膨胀。
  3. 使用模板的目的是用模板去约束类型,并减少重复的信息,易于维护。不追求语法上的技巧。不强求用最好(最新)的方法。

最终我花了一整个晚上才实现出来。代码不算太多,功能代码 100 来行,再加上 100 来行的简单使用案例(和之前的 C 版本基本功能一致)。我编写 C++ 模板代码不太熟练,中间编译错误的反复修正耽误了不少时间。但是,还是有大比例的时间在思考怎么用模板工具去绕出我想要的功能:类型约束检查。

这里是我最终的版本

最后,我们可以用一个简单的 for 语言迭代特定的组件数组。

void
particle_system::update_life(float dt) {
    int index = 0;
    for (auto &life : attrib()) {
        printf("lifetime: %fn", life);
        life -= dt;
        if (life <= 0) {
            printf("REMOVE %dn", index);
            remove(index);
        }
        ++index;
    }
}

这段代码是迭代一个 float 的 life 数组,把其中每个 float 递减 deltaT 。如果 life 小于等于 0 ,就标记对象为删除。

其中迭代支持标准的 C++ 形式 for (auto &life : attrib<lifetime>()) ,除去类型转换处理,它本质上就是在遍历一个 float [] 。

标记对象为删除也只需要 remove<lifetime>(index) 此处的 index 为 life 组件在 lifetime 属性数组中的当前序号。


另一个使用案例见 particle_system::update_print 的实现。

void
particle_system::update_print() {
    int n = size(TAG_PRINT);
    for (int i = 0;i<n;i++) {
        const value *v = sibling<value>(TAG_PRINT, i);
        if (v) {
            printf("Value = %d ", v->value);
        }
        const object *obj = sibling<object *>(TAG_PRINT, i);
        if (obj) {
            printf("Object = %d ", obj->value());
        }
        printf("tParticle %dn", i);
    }
}

它用 TAG_PRINT 遍历所有的有这个 tag 的对象。因为这是个虚拟 tag 并没有对应的组件,所以在这里并没有对应的数组容器。我们用 size(TAG_PRINT) 取出个数,然后 for i = 0 .. n 即可。在迭代中,可用 sibling<类型> 取到关联的组件。


最后是添加粒子的 api :

add({
    push_back(lifetime(20)),
    push_back(value { 0, 1 }),
    push_back(new object(42)),
    TAG_PRINT,
});

这些 push_back 会根据类型把组件添加到正确的容器中。同时, add 会将信息组织起来向管理器注册新的粒子。此处还可以打上虚拟 tag ,比如 TAG_PRINT

但实际上,这种用法的实际用途有限。因为我们最终并不会使用 C++ 代码静态的创建固定的粒子。而是发射器根据预定义的数据结构去分步创建。


在我花了一晚上时间(前后改了三版)最终完成了现在的版本后,我陷入了深深的怀疑中。仅仅是为了增加更严格的类型约束而消耗很多的精力到底有什么意义。其实我并没有完成太多实际的功能。当然代码量不大,算不上臃肿,但精力的开销却是能直接感受到的。大比例的代码和时间都是在想办法(通过不那么直观的形式)告诉编译器,这里应该增加怎样的约束,而不是完成运行时的某个特性。

另外,还不断的需要和内聚度做斗争(为此我调整了几次代码)。我期望不要把过多实现细节暴露在外面(尽量少在头文件中暴露出内部细节)。而 C++ 在这方面提供的语言层面的帮助却比较少:如果想定义方法针对类中的数据做操作,就需要把方法定义在类声明中暴露在头文件里。模板的前置声明写起来也很繁琐。

即使抛开不太熟悉语言工具这个因素,我认为经验丰富其实也并不会减少太多精力去完成它。而代码质量却变得不那么显而易见。我只能说,现在的代码中冗余的信息并不算多,绝大对数代码行都在履行预设的任务。没有太多的信息重复。但是,到底够不够简单易维护却是很难判断的。至少一眼看上去这百来行代码并不是特别容易理解。这是违背我的美学的。可能应归结到我的能力和经验欠缺上,C++ 代码暂时写不出更好。

但更好(简单易理解)的门槛是不是有点高?


立即购买:淘宝网]]>
IT技术精华 2020-11-28 01:11:06
49751 美团无人车引擎在仿真中的实践 1. 引言

过去几年,自动驾驶技术有了飞速发展。国内也出现了许多自动驾驶创业企业,这些公司以百度开源项目Apollo为起点,大都可以直接进行公开道路测试,公开道路测试也成为促进技术进步的主要方法。基础问题得以解决之后,行业面临的更多是长尾问题,依靠路测驱动自动驾驶能力建设的方式变得不再高效,离线仿真的地位日益凸显。行业头部企业在仿真的投入十分巨大,Waymo公司2019年公布的仿真里程是100亿英里,是路测里程的1000倍。

相应地,美团无人车团队在仿真上的投入也在逐渐增大。在仿真平台的建设中,团队发现公开道路测试和仿真测试看似相似,实际上差异巨大:在车载环境下,为了确保系统的稳定运行,通常要保证一定资源处于空闲状态;仿真环境则不同,如何高效利用资源,如何实现压榨资源的同时确保仿真结果与路测结果一致成为了关键目标。在应对这些挑战的过程中,美团提出了无人车引擎的概念,将车载与离线环境的差异隔离起来:功能模块无需任何更改便可以满足两种场景的需要。

本文首先会介绍无人车引擎的概念,并以仿真环境面临的挑战为线索介绍美团无人车引擎的核心设计。

02 无人车引擎

概念

无人车引擎是自动驾驶的基础设施,在机制、工具和计算模型上对功能模块提供支持,隔离自动驾驶所处环境,使各功能模块专注于自身功能。

在机制层,他为各功能模块提供通信、调度、数据、配置、异常监控等支持。

在应用层,引擎为各功能模块提供调试、可视化、性能调优、效果评估等工具支持。

在模块层,引擎为各功能模块提供统一的计算模型和运行环境,确保他们在车上环境、分布式环境、调试环境下的行为一致。

美团无人车引擎的架构图如下:

图1 无人车引擎布局

如图1所示,作为引擎支撑的主要部分,Perception、Localization、Planning等是自动驾驶系统中重要的功能模块,它们实现了无人车系统的核心功能。引擎则在机制和工具,上下两个方向上支撑他们:各功能模块按照引擎的规范开发,直接或者间接地使用引擎机制层提供的功能并自然而然地获得工具的支持。比如,功能模块只要使用引擎的通信工具,就能直接获得数据落盘、性能报表调试信息可视化的支持,同时基于这些路测数据,在仿真环境下,功能模块会自动获得单步调试、效果评估等功能支持。

自动驾驶引擎面临的挑战

图1中所列举的功能是引擎的基础组成部分,引擎所提供的远不止于此,对于多种环境的支持才是美团无人车团队引入引擎概念的真正原因。前面提到,无人车首先运行在车载系统中,随着技术和环境的变化,更多地运行于仿真环境下,二者截然不同。车载环境下,无人车系统的运行环境较好,为了保障各功能模块能够正常运行,CPU、GPU、内存等资源要提供一定程度的冗余。而仿真环境的要求完全不同:从用户的角度看,仿真的用户是工程师,他们期望仿真任务能够在确定时间内完成尽量多的任务;从集群的角度看,他们希望仿真能够尽量提升资源利用率。接下来的部分将介绍无人车系统在这两类环境下会面临哪些挑战,以及美团无人车团队如何通过引擎应对这些挑战。

行为一致性的挑战

早期,美团无人车团队依赖于ROS搭建无人车系统,在车载环境下,ROS的表现合格。然而在开始仿真建设后,团队遇到很多问题,其中最突出的是“行为一致性问题”,这个问题具体是指:无人车系统在运行过程中,当出现系统资源的变化,行为也随之发生变化。比如,当仿真任务在一台机器上运行时,系统产生的结果和这台机器的状态有关,这台机器被独占地使用或是和其它任务同时运行,结果会有差异。而且,即使不考虑资源利用率,让仿真任务独占机器资源,同一个任务运行两次,结果也会有微弱的扰动。

更严重情况发生在离线环境,此情境追求资源利用率的最大化,意味着计算资源的十分紧张,扰动将变得不再轻微,结果将变得更不可靠,仿真的结果也就失去了价值。

因此,如何在车上和离线两套截然不同的环境下确保结果的一致性,是仿真引擎必须解决的问题。此问题由以下两个原因造成:一是功能模块时序的不一致;二是功能模块内部执行的不一致。

时序一致性

为了介绍什么是时序一致性,首先要介绍一下无人车系统中时序的概念。

无人车系统由多个功能模块组成,功能模块之间有数据依赖关系,比如Perception依赖于 Lidar、Camera的数据,Prediction依赖于Prediction的输出。不同模块的触发条件不同,比如 Planning是依据时钟触发的而Prediction是依赖于Perception的数据触发的。由数据关系和触发条件形成的功能模块的执行顺序就是自动驾驶系统的时序。在理想情况下,每个模块都能在满足触发条件时立刻执行并在预期的时间内完成任务,也就是说,只要保留各模块的输出就可以完全复现线上的问题,离线仿真出现的问题在路测时也必然出现。

图2 无人车系统理想时序

然而现实情况远比这复杂,举例来说,当无人车经过拥堵路段时,Perception需要处理的数据会显著增多,Planning也可能因为交通参与者过多导致耗时增长,时序必然与理想情况不符合。如下图3所示,在车载环境下这种行为方式是没问题的,然而在仿真环境时却会导致严重后果:每一次计算环境的些许变化都有可能导致时序的变化,进而导致系统行为的差异。

图3 无人车系统实际时序

这就是时序一致性问题。为了解决这种问题,美团无人车引入了调度器,时序的一致性由调度器保证。此外,引擎按照不同的应用场景,进一步细化了调度器的种类。其中最简单的调度器是“在线调度器”,它的目标只有一个:在功能模块处于Ready状态时执行它,车载系统中就是使用的这种调度器,它的行为方式也与ROS类似,不过他会记录下调度时序以备使用。除此之外,引擎还提供一组离线调度器,以应对不同的使用场景。这里在线和离线的差异根据数据来源判断,如果数据来自传感器那么就是在线调度器;如果数据来自路测记录那就是离线调度器,具体分类如下图4所示:

图4 调度器分类

以下是美团无人车引擎提供的调度器种类及他们的使用场景:

  • 在线调度器:在满足触发条件时立即触发功能模块,通常在车载环境下会使用;
  • 复现调度器:按照调度器保存的调度信息复现调度时序,在调试时或复现路测场景时使用;
  • 理想调度器:按照理想时序调度资源,通常在仿真时使用;
  • 条件驱动调度器:在条件满足时调度功能模块运行。在这种调度方式下,功能模块的调度密度介于理想调度器和复现调度器之间,他的实现也相对简单,是应用最广泛的调度器。

在他们的帮助下,功能模块执行的时序就能得到保障:只要调度器和输入数据不变,那么无论计算环境如何变化,各功能模块的执行时序总能保持一致。

功能模块的计算模型

时序一致性除了需要调度保证之外,功能模块的内部计算必须是受到调度器调度的。功能模块必须在调度器允许时才能开始执行,在结束时调度器能得到通知。如果存在脱离调度器之外的计算线程,那么系统的一致性必然无法保证。为此,引擎引入了标准计算模型,任何一个功能模块都有应该遵守这个计算模型,从而获得引擎包括一致性保障、单步调试支持、信息可视化等功能的支持。

标准计算模型如下:每一个功能模块都有且仅有一个计算过程并以迭代为单位,每一次调度完成一帧的计算。当然引擎并不控制帧计算内部的细节,帧计算内部的优化由功能模块负责。

图5 功能模块的标准模型

标准模型的定义并不一定符合每一个功能模块的实际情况:比如Localization,它订阅多类频率不同的传感器数据并以不同的频率输出。在实践中,引擎通过对Localization功能的重新拆分实现了标准化。此外,对于像Perception这类计算量很大、同时兼具异构计算的功能模块来说,多线程,异步I/O的机制必须引入,引擎同时提供了相应的支持确保符合标准模型。

在实践过程中,美团无人车团队花费了相当时间来完成这些改造。改造完成后仿真结果的权威性得到了加强,更重要的是:系统的行为不再受外部资源(GPU、CPU、内存等)的影响,这也为离线环境提升资源利用率扫清了障碍。接下来介绍无人车引擎如何在功能模块完全无感的情况下提升资源利用率。

04 资源利用率问题

前面提到过,车载系统和仿真系统环境差异很大:车载系统为了追求系统的平稳运行会保证关键资源有一定程度的富裕;对于仿真系统,保留idle就是对资源的浪费。在系统的一致性得到保障之后,资源利用率才能成为引擎的优化目标。优化资源利用率包含了很多方面,比如数据调度等,由于篇幅所限,这里只介绍与引擎相关的优化工作。接下来的部分,将根据无人车系统在仿真环境运行时的特点进行优化,他们分别是资源需求不均、功能模块的重复计算、GPU/CPU计算不平衡。

数据需求不均匀

从数据的输入规模上讲,各功能模块是极不均衡的:Perception和Localization依赖于Lidar和Camera数据,数据使用量占到系统的 85% 以上(按照数据存储的规模计算,忽略中间数据,具体比例与开启的Camera相关,此处给出概数)。从资源消耗上讲,Perception和Prediction消耗较多的 GPU 计算资源。为了提升云计算资源的效率,无人车引擎必须支持分布式部署:即一套自动驾驶系统分别部署与多台机器甚至是跨机房的机器之上的。

图6 分布式部署

为了实现分布式部署,引擎参考了计算图模型的概念,采用了类似于Tensorflow的设计:将功能模块分成了Node和Module两个部分。其中Node负责定义依赖关系,而Module负责完成计算。对于远程部署的Module来说,引擎提供了ADVContext和Node Stub 的概念用于协助Module完成运算,对于Module而言,它对于自身处于环境(远程或者本地)一无所知。

基于图7的设计,自动驾驶系统有了分布式部署的能力:一套自动驾驶系统可以运行于一组机器之上。提升离线效率的努力不再局限于单台机器,无人车系统的离线优化获得了更多的手段和更广的空间。

重复计算

仿真任务分成多种类型,即有运行单个模块的任务,也有同时执行 Perception、Prediction、Planning的任务。对于同时运行多个Module的任务,放在集群的角度看,很多计算都是重复的。试想一个场景:Planning引入新方法,工程师希望能够在最新Perception版本上的获得新方法的效果评估结果。对于仿真而言,这是一个经典场景,常用的方法是离线执行 Perception、Prediction和Planning三个模块并执行Evaluation产生报表、评估结果。

图8 分模块Evaluation

一般而言,Perception的结果受到数据和本身算法迭代的影响,当 Planning的迭代时,Perception的结果不会受到影响,它的输出完全可以复用。得益于Node和Module概念的分离,Perception Node所绑定的Module完全可以是一个非计算单元,而是一个数据服务Module。

在美团无人车数据平台和无人车引擎共同努力下,通过Data Service Module, 这个常见的仿真任务的流程在工程师感知不到的情况下变成了图9这样。不同版本的Perception的输出结果被保存下来,Prediction和Planning只要使用之前的结果,避免了 Perception的反复计算。

图9 数据复用

GPU计算分流

无人车系统是一个同时具备重度CPU计算和重度GPU计算系统,两部分的计算是不平衡的。引擎为了提升GPU资源的利用效率,在内部集成了模型管理的功能同时提供了本地和远程两种Prediction的机制。再结合分布式部署方式,系统可以完全部署于CPU集群之上,模型相关的计算可以通过RPC请求在Model Serving上完成。通过GPU和CPU计算的隔离,引擎帮助提升了GPU和CPU计算资源的利用率。

05 结论

在持续的实践中,美团无人配送团队抽离出一套自动驾驶引擎,为功能模块提供机制和工具的同时,它还提供了对车载(低时延)和仿真(高吞吐)两套环境的适配。此外,配合美团的大数据基础设施以及在此基础之上专为无人车建立的数据平台,美团无人车逐步建立了完善的自动驾驶基础设施。未来,希望在引擎的帮助下能够隔离功能模块、计算平台、运行环境,使得自动驾驶能力迭代与自动驾驶落地应用两个方向上的工作能够独立开展,齐头并进,加快美团无人车的落地步伐。

关于美团无人配送

美团无人车配送中心成立于2016年,由美团首席科学家夏华夏博士领导。美团无人车配送围绕美团外卖、美团跑腿等核心业务,通过与现有复杂配送流程的结合,形成了无人配送整体解决方案,满足在楼宇、园区、公开道路等不同场景下最后三公里的外卖即时配送需求,提升配送效率和用户体验,最终实现“用无人配送让服务触达世界每个角落”的愿景。

招聘信息

美团无人车配送中心大量岗位持续招聘中,诚招算法/系统/硬件开发工程师及专家。欢迎感兴趣的同学发送简历至:ai.hr@meituan.com(邮件标题注明:美团无人车团队)。


立即购买:淘宝网]]>
IT技术精华 2020-11-27 15:11:03
49719 科技爱好者周刊(第 135 期):什么行业适合创业? 这里记录每周值得分享的科技内容,周五发布。

本杂志开源(GitHub: ruanyf/weekly),欢迎提交 issue,投稿或推荐科技内容。

周刊讨论区的帖子《谁在招人?》,提供大量程序员就业信息,欢迎访问或发布工作/实习岗位。

封面图

重庆北碚区新建的彩色停车场,地面采用红、黄、蓝三色陶瓷颗粒罩面。这样除了增加视觉美感,还能更好地保护道路基面,增加了路面摩擦力,提高了停车安全系数。(新华网

本周话题:什么行业适合创业?

一个美国风险投资家解释他如何选择风投项目,我觉得很新奇。

他画了一个坐标系,然后把项目的行业放在里面。

上图的坐标系,竖轴是交易规模,位置越向上,表示交易金额越高;横轴是交易频率,位置越向右,表示交易次数越多。

根据这两个坐标轴,所有行业可以分成四类。

  • 交易价格高、交易频率低的行业(汽车、房地产、婚纱摄影、高端体检)
  • 交易价格低、交易频率低的行业(水管工、锁匠、体育用品、服饰)
  • 交易价格低、交易频率高的行业(出租车、餐饮)
  • 交易价格高、交易频率高的行业(......有这样的行业吗?)

举例来说,房地产的价格很贵,普通人多年才能购买一套,所以位置在坐标系的左上角;小餐馆的价格比较低,你经常会去,所以位置在坐标系的右下角。

那位风险投资家说,投资项目要么在左上角(高价低频行业),要么在右下角(低价高频行业),不要去碰左下角(低价低频行业)。 理由很简单:如果一个行业的交易行为是"低价低频",怎么赚大钱呢?

比如,出租车的网络平台可以成功,开锁的网络平台不可能成功,原因是打车是"低价高频"行为,开锁是"低价低频"行为。

他进一步说,"高价低频行业"适合使用垂直网站,因为单价高,可以把上下游产业整合进去,提升获利; "低价高频行业"适合使用水平网站,因为单价低,需要覆盖较广的市场。

我觉得,说得很有道理,分享给大家。国内成功的电子商务公司,确实都是如此。淘宝、滴滴打车、美团这样的水平网站,确实都是服务"低价高频行业";贝壳找房、汽车之家这样的垂直网站,则是服务"高价低频行业"。

《前端与图形学》公开课

图形学大概是最专业的前端方向,所要求的知识技能与其他方向截然不同,属于独立的前端技术岗位。

它正变得越来越重要,许多内容、数据、用户交互只有通过图形展示,才能取得满意的效果。

一般情况下,我们使用的都是封装好的图形库,很少关注底层的图形技术,更不要说搞懂背后的理论了。

但是,随着定制需求、性能、UI 的现代感和设计感......这些要求越来越高,图形学终究是优秀前端工程师绕不开的领域。 前端工程师里面,最抢手的人才就是图形工程师,所有大厂都非常稀缺。

市场上的图形课程并不多,高质量的更是凤毛麟角。本周向大家介绍的,就是开课吧出品的 《大牛必备:前端与图形学》公开课 。前阿里 P8(高级技术专家)Winter 主讲,原价199元,推广期间可以0元领取!

这个课程时长为120分钟,全部都是图形学内容,将帮助大家:

  1. 深刻理解前端与图形学的关系,图形工程师发展前景。
  2. 学习如何将图形学知识应用到前端,为业务创造价值。
  3. 学习对图形学基础设施的建设,为技术进阶做铺垫。

微信扫描下面二维码,现在就可以 0 元领取 《前端与图形学》公开课。不设条件 (没有转发和分享要求),仅限 200 名,送完即止!

资讯

1、家用办公室

疫情期间,很多人在家办公,但是家里环境可能不适合办公。为了解决这个问题,一家美国创业公司推出了家用办公室。

平时,这套家具就是靠在一起的两个柜子(上图)。办公时,将这两个柜子拉开,就会得到一个专业的办公环境(下图),这样就比较节省空间。

2、如意桥

今年9月25日,浙江省台州市神仙居景区的如意桥对外开放,至今已经吸引了20万人次的游客。该桥全长100米,垂直高度140多米,由两个交错起伏的曲面构成,部分桥面为全透明玻璃设计。

从上空俯瞰,它宛如一柄悬在空中的玉如意。

从侧面远眺,它又像一条飘逸的彩带。

桥两端设计成圆盘形状了,主要是为了给一棵从悬崖底长上来的甜槠树留着生长空间,它已经长到十多米高了。

3、中国人脸识别第一案

2019年,杭州野生动物世界升级动物园年卡,从指纹识别改为人脸识别,要求所有年卡持有人提供自己的人脸数据,否则年卡将失效。一位消费者不愿提供自己的人脸数据,协商无效后,将动物园告上了法庭。

最近,杭州市富阳人民法院一审判决,动物园败诉。但是,法院并没有裁定动物园不得强制收集人脸数据(相反是认可这种行为的),而是裁定动物园不得单方面变更已经达成的合约,这让人相当失望。

4、阿雷西博望远镜将拆除

美国国家科学基金会(NSF)今天宣布,由于过于老旧,维修有危险,阿雷西博望远镜(Arecibo)将拆除。该望远镜位于波多黎各,1963年落成,在2016年中国天眼建成前,一直是世界最大的射电望远镜。

阿雷西博望远镜的主体是一堆重达900吨的仪器,悬挂在305米宽的碟盘上方的137米处。波多黎各是一个加勒比海岛,有频繁的飓风和地震,导致悬挂仪器的12根钢缆损坏严重,有一根已经断了,三个支撑塔也破败不堪,有坍塌的可能。

接下来几周,工程公司将制定拆除计划,可能采用爆炸,一次性将悬挂的仪器炸到地面。

5、干冰紧缺

辉瑞公司的 Covid-19 疫苗即将上市,全世界有巨大的需求。问题是,它需要零下70度储存和运输。最简单和成本最低的冷藏方法,就是使用干冰,它可以让温度保持在零下78.5度。

美国各州现在都在紧急采购干冰,导致供应紧缺。干冰是乙醇生产的副产品,正常情况下,美国和加拿大每天可以供应3万吨,而疫苗的储存估计只需要用到其中5%。但是疫情期间,乙醇生产急剧减少,使得干冰生产也不足。

6、一句话消息

  • 美国陆军为军犬配备了实验性的 AR 眼镜。行动过程中,指令会显示在眼镜上,远程指挥军犬的行动。

  • 上海张江出现了无人餐车,扫码开门,取餐后会自动扣款,拿多少扣多少。

  • 瑞典沃尔沃汽车公司使用起重机,将10辆汽车从30米高空自由下落,车头直接着地,测试极端情况下车辆的安全状况。

文章

1、我开发 SaaS 的工具和服务(英文)

作者列出了自己开发 SaaS 服务用到的各种工具和服务,包括前端工具和后端工具。

2、开源项目维护者的九个阶段(英文)

一个成功的开源项目,它的维护者可能需要经历九个阶段,第一个阶段是发明家,最后一个阶段是光荣退休。

3、学校芯片工艺实验室参观记(中文)

一个大一新生参观学院芯片工艺实验室的记录。

4、北京四合院改造成幼儿园(中文)

北京 MAD 建筑事务所将一个四合院,改建成了可容纳390位儿童的幼儿园。

5、如何写出容易维护的 Makefile(英文)

本文通过一个简单的例子,介绍 Makefile 的基本知识和最佳实践。

6、Java 8 之后的语法增强(英文)

本文介绍了从 Java 9 到 Java 15 的主要新增语法。

7、如何在安卓手机检查屏幕截图?(英文)

有些安卓 App 不允许用户截图,实现方法很简单,就是检查 Screenshots 文件夹是否添加了新图像。

8、如何用 Podman 替代 Docker(英文)

Podman 是 RedHat 公司开发的容器引擎,用来替代 Docker。本文是一篇简单的上手指南。

9、用 JS 写一个 JS 解释器(中文)

本文解释了 JS 解释器如何将源代码转换成 AST(抽象语法树),并给出了一个简单的、便于理解的解释器实现。(@webfansplz 投稿)

工具

1、 Your First GitHub Commit

这个网站可以找出你的第一个 GitHub 提交。

2、Perfect Dark Mode

一个 JS 库,一键切换网页的亮模式和暗模式。

3、rector

一个 PHP 代码的编译器,可以将 8.x 版本代码编译成 7.x 版本,类似于 JavaScript 语言的 Babel 工具,这里还有一篇介绍文章

4、gping

ping 命令的替代品,带有图形界面。

5、Edit CSV Online

一个编辑 CSV 文件的在线工具。

6、Logseq

一个开源的在线笔记工具,具有强大的知识整理功能。特点是所有数据都储存在你的 GitHub 仓库里面,网站本身不保存用户数据。

7、Drumbit

一个在线的鼓点模拟器。

8、GitHub Pages URL Shortener

使用 GitHub Pages 托管你自己的短网址服务,不需要后端和数据库,非常有创意的小工具。

9、SVGOMG

一个在线工具,用来压缩 SVG 图像文件。

10、Code with me

IntelliJ IDEA 的官方插件,允许多人在 IDE 里面实时协同编程。(@dengshenkk 投稿

资源

1、Docker 指南

一篇英文的 Docker 教程,解释最重要的一些概念,帮助读者学会使用 Docker 进行应用程序开发。

2、废弃媒体博物馆

这个网站介绍各种已经淘汰的数据储存媒介,比如纸带、录像带、VCD、磁盘等等。

3、Play with Go

Go 语言教程网站,提供一系列互动式入门教程。

4、互联网围棋数据库

可以搜索棋手姓名,查找棋谱,进行下载或在线打谱。(@yujiff 投稿)

5、Linux 命令大全

一个中文文档库,收集了 Linux 的各种命令,收集了命令的解释和配置项示例。(@1228857713 投稿)

6、《编程之道》中英双语版

美国资深程序员 Geoffrey James 在1987年写了《编程之道》(The Tao of Programming),曾一度成为美国程序员圈的文化热点,书中的佳句和故事被大家津津乐道。(@yikeke 投稿)

图片

1、2020年度照片

2020年即将结束,各种年度照片的评选也纷纷揭晓。

年度特写摄影比赛

冬日的清晨,蘑菇的菌盖上结满了霜。

一只黑蚂蚁正在搬运飞蛾的卵。

天气摄影比赛

孟加拉国的严重干旱。

贝加尔湖的冰面在阳光下,反射宝石般的颜色。

克罗地亚城市上方锅子倒扣般的云。

2、NBA 2K14 与 NBA 2K21 的变化

七年过去了,游戏 NBA 2014 与 NBA 2021 里面的球员对比。

3、世界生育率变化

下面是1900年~2016年世界各国生育率的变化情况,中间的那条横线是平衡生育率(2.05),只要低于这条线,总人口就将萎缩。

可以看到,过去100年全世界的生育率都在下降。所以,未来的世界总人口可能根本不会出现膨胀。

1900年的生育率。

2016年的生育率。

文摘

1、瘦素与青春期

作者:比尔·布赖森(摘自《人体简史》一书)

人类的进化过程中,一直是饮食匮乏,历史上从没出现过饮食无忧。所以,人类没有进化出任何机制,告诉你什么时候应该停止进食。

这就是为什么我们经常一吃东西就停不下来,因为我们的身体没有防止过量进食的功能。

1995年,科学家发现了一种新的激素,叫做瘦素,它可以让人体启动消耗脂肪的过程。

具体来说,它的用途主要是告诉大脑,身体已经有足够的能量储备,可以应对相对苛刻的挑战,比如怀孕或开始进入青春期。如果瘦素匮乏,此类过程就不会启动。这就是为什么患有厌食症的年轻人,青春期大多来得很迟。

古代的人们要到十六七岁才进入青春期。现在不一样了,青春期开始得比历史上任何时期都早,往往11岁就进入青春期。原因几乎可以肯定是因为营养得到了改善,人体内的瘦素分泌大大提前,大脑很早就得到通知,可以开始青春期了。

2、苹果商店的30%提成

自从计算机诞生,开发者一直有分发软件的自由,可以自主地将软件交付给用户。从来没有一个机构,决定哪个软件能分发,哪个不能;更没有一个机构,要求开发者将很大一部分收入上交。

视频游戏机是一个例外。游戏机制造商决定了什么游戏可以上架,但游戏机是专用设备,因此是封闭的生态系统。个人计算机不一样,一直是开放生态,这大大推动了创新。

后来,iPhone 应用商店出现了,一切都变了。只有得到苹果公司的批准,软件才能上架,并且苹果要对软件的收入提成,费用是每年99美元 + 总收入的30%。相比之下,美国的信用卡费用是每笔交易的 2.9% + 0.30美元。

30%这个提成比例,是效仿视频游戏机制造商的提成比例。苹果公司声称,iPhone 是封闭的生态系统,因为电话属于专用设备,主要功能是拨打电话,必须保护其免受恶意软件的侵害。所以,30%的费用是合理的,用来支付应用商店审核应用的成本。

此后,谷歌对安卓应用商店也实施了类似的收费比例,因此问题变得越来越严重,从 Steam 到三星手机,几乎每家应用商店都在效仿这种提成模式。

现在,Windows 和 macOS 也已经有了应用商店,未来也有可能控制哪些软件可以在 PC 上分发,侵蚀开发者的自由。尤其是 Mac 电脑,安装未经苹果批准的第三方软件时,默认会跳出警告,阻止你安装。

我们必须停止这种趋势,防止大公司控制软件的分发。

智能电话的主要功能已经不是电话了,而是便携式计算机。计算机是一般性计算设备,不是封闭的生态系统,将它们封闭起来,并不会让用户受益。

2019年,iOS 应用商店创造了约500亿美元的销售额,苹果从中获利约150亿美元。我们不禁要问,维护应用商店真的需要这么高的成本吗?这到底是合理的利润,还是苹果在对用户收税?

如果苹果或其他公司,真的要通过收费来弥补应用商店的成本,则应收取固定金额的费用,而不是按比例提成。

我们需要强有力的政府监管,保护市场免受大型企业的垄断和反竞争行为的侵害。解决方案很简单:法律应该明确智能手机和 PC 是通用计算平台,具有开放性,开发者有在这些设备分发软件的自由,前提是软件不会损害用户(恶意软件,间谍软件等)。

言论

1、

中国模式的经济起飞,实际上就是美国模式。日本和亚洲四小龙都以此模式来发展经济,然后中国模仿了它们。

-- Hacker News 读者

2、

浏览器起初只是一个简单的远程文档查看器,后来被迫成为一个应用程序平台。由于浏览器一开始没设计为这个用途,后来互联网的诸多问题都是这个事实造成的。

-- ThoughtWorks 技术雷达(2020年10月)

3、

我完成日常工作后,每晚还要花四五个小时在 Bootstrap 上工作。下班后,我不能和别人约晚饭,因为我觉得这会让用户失望:我不应该出去玩耍,我应该在Bootstrap上工作!

-- 桑顿(Jacob Thornton),开源 CSS 框架 Bootstrap 的创造者之一,他已经在该项目上工作了九年。

4、

我们的 Covid-19 疫苗研究,没有要政府的钱。因为那些钱是附带条件的,你必须不断报告进展。

政府想听报告,而我根本不想做任何报告。我让科学家放手去做,专注于生产疫苗而不是提供进度报告,只担心科学挑战,而不必担心其他任何事情。

-- 辉瑞公司 CEO

5、

近处观看时,如果要让人眼察觉不到显示屏的颗粒感 ,显示屏的像素密度需要超过人眼的分辨率,必须高于每英寸285像素的像素密度。4K 显示器都达不到这个门槛,27英寸 4K 显示器的像素密度约为163 PPI,更大显示屏的像素密度就更低了。

实际上,市场上只有一种大型显示器能够接近这种密度,那就是27英寸 iMac 的 5K 显示器,分辨率 5120 x 2800,像素密度220 PPI。

-- 《如何自制高像素画框》

6、

太多的人把辩论看作是战斗,要分出胜负。其实,辩论应该是一种合作,两个人合作发现真理。

-- 《建设性辩论的初学者指南》

回顾

2019年的本周(第 84 期):一次性工作招聘,用完你就丢

2018年的本周(第 32 期):砌砖头的三种角度

订阅

这个周刊每周五发布,同步更新在阮一峰的网络日志微信公众号

微信搜索"阮一峰的网络日志"或者扫描二维码,即可订阅。

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期: 2020年11月27日

立即购买:淘宝网]]>
IT技术精华 2020-11-27 10:11:03