0%

梯度提升树

首先考虑梯度提升树,考虑一个有nn个样本,每个样本有mm个特征的数据集D={(xi,yi)}\mathcal{D} = \{(\mathrm{x}_i, y_i)\},一个集成树模型实际上得到的使用K个具有可加性质的函数,得到的输出对应如下所示:

y^i=ϕ(xi)=k=1Kfk(xi),fkF\hat{y}_i = \phi(\mathrm{x}_i) = \sum_{k=1}^K f_k(\mathrm{x}_i),\quad f_k \in \mathcal{F}

对于每一棵树而言,一个输入会被映射到一个对应的叶节点,这个节点上的权重就对应这个输入的结果。在这里目标函数使用被正则化的形式:

L(ϕ)=il(y^i,yi)+KΩ(fk)\mathcal{L}(\phi) = \sum_{i}l(\hat y_i, y_i) + \sum_K \Omega(f_k)

whereΩ(f)=γT+12λw2\text{where} \quad \Omega(f) = \gamma T + \frac12 \lambda \|w\|^2

其中前半部分ll代表的是损失函数,用来量化预测值与真实值之间的差距,后者是正则化项,用来控制模型的复杂度,防止过拟合。对于树模型而言,正则化项的第一项控制叶节点的数量,后一项控制每个叶节点的权重。如果去掉正则化项,实际上就是普通的梯度提升树。

在对于第tt颗树的时候,我们需要优化的目标函数实际上以下式子:

L(t)=i=1nl(yi,y^i(t1)+ft(xi))+Ω(ft)\mathcal{L}^{(t)} = \sum_{i=1}^n l(y_i, \hat y_i^{(t-1)} + f_t(\mathrm{x}_i)) + \Omega(f_t)

将损失函数展开到二阶近似:

L(t)i=1n[l(yi,y^i(t1))+gift(xi)+12hift2(xi)]+Ω(ft)\mathcal{L}^{(t)} \approx \sum_{i=1}^n \left[ l(y_i, \hat y_i^{(t-1)}) + g_i f_t(\mathrm{x}_i) + \frac12 h_i f_t^2(\mathrm{x}_i)\right] + \Omega(f_t)

其中y^i(t1)\hat y_i^{(t-1)}是前t1t-1颗树的结果,在当前的优化实际上是一个常数,将其移除之后得到tt步的优化函数为:

L~(t)=i=1n[gift(xi)+12hift2(xi)]+Ω(ft)\tilde{\mathcal{L}}^{(t)} = \sum_{i=1}^n \left[ g_i f_t(\mathrm{x}_i) + \frac12 h_i f_t^2(\mathrm{x}_i) \right] + \Omega(f_t)

定义Ij={jq(xi)=j}I_j = \{j| q(\mathrm{x}_i) = j\} 为叶节点jj上面对应的样本集,于是可以修改求和形式如下:

L~(t)=i=1n[gift(xi)+12hift2(xi)]+Ω(ft)=j=1TiIj[gift(xi)+12hift2(xi)]+γT+12λj=1Twj2=j=1TiIj[giwj+12hiwj2]+γT+12λj=1Twj2=j=1T[(iIjgi)wj+12(iIjhi+λ)wj2]+γT\begin{aligned} \tilde{\mathcal{L}}^{(t)} &= \sum_{i=1}^n \left[ g_i f_t(\mathrm{x}_i) + \frac12 h_i f_t^2(\mathrm{x}_i) \right] + \Omega(f_t) \\ &= \sum_{j=1}^T \sum_{i \in I_j} \left[ g_i f_t(\mathrm{x}_i) + \frac12 h_i f_t^2(\mathrm{x}_i) \right] + \gamma T + \frac12 \lambda \sum_{j=1}^T w_j^2 \\ &= \sum_{j=1}^T \sum_{i \in I_j} \left[ g_i w_j + \frac12 h_i w_j^2 \right] + \gamma T + \frac12 \lambda \sum_{j=1}^T w_j^2 \\ &= \sum_{j=1}^T \left[ \left(\sum_{i \in I_j} g_i\right) w_j + \frac12 \left(\sum_{i \in I_j}h_i + \lambda\right) w_j^2 \right] + \gamma T \end{aligned}

当对于一个确定的树结构,γT\gamma T为常量,前面这一项对应于wjw_j的一个二次表达式,可以得到最优解为:

wj=iIjgiiIjhi+λw_j^\star = - \frac{\sum_{i \in I_j} g_i}{\sum_{i \in I_j}h_i + \lambda}

带入可以知道最优的值为:

L~(t)(q)=12j=1T(iIjgi)2iIjhi+λ+γT\tilde{\mathcal{L}}^{(t)}(q) = - \frac12 \sum_{j=1}^T \frac{(\sum_{i \in I_j} g_i)^2}{\sum_{i \in I_j}h_i + \lambda} + \gamma T

上式仅仅与树结构qq有关,所以可以作为一个树结构的度量,越小说明这个树结构越好。由于没有办法穷举所有可能的树结构,所以只能贪心地对于树结构去改进,增添新的分支。假设我们希望把一个节点分离成两个子集ILI_LIRI_R那么这个分裂会带来的L~\tilde{\mathcal{L}}的减少就是:

Lsplit=LbeforeLafter=12[(iILgi)2iILhi+λ+(iIRgi)2iIRhi+λ(iIgi)2iIhi+λ]γ\begin{aligned} \mathcal{L}_{\text{split}} &= \mathcal{L}_{\text{before}} - \mathcal{L}_{\text{after}} \\ &= \frac12 \left[ \frac{(\sum_{i \in I_L} g_i)^2}{\sum_{i \in I_L}h_i + \lambda} + \frac{(\sum_{i \in I_R} g_i)^2}{\sum_{i \in I_R}h_i + \lambda} - \frac{(\sum_{i \in I} g_i)^2}{\sum_{i \in I}h_i + \lambda} \right] - \gamma \end{aligned}

这个值即为分裂带来的增益,应当越大越好,其中前面一项是因为分裂所带来的提升,后面一项是对于分裂使得模型复杂度增加的惩罚。所以γ\gamma相当于给节点分裂设定了阈值,只有当分裂带来的增益超过这个阈值,才会进行树分裂,起到了剪枝的效果。

节点分裂算法

精确贪心算法

关键问题就是如何找到最优的分裂方案来获得最大的分裂增益,最直观的方法就是进行遍历,只要对于数据所有可能的分裂方式进行一次遍历,就可以从中找到增益最大的分裂方式。为了算法能够执行的更加高效,我们需要在最开始对于数据进行一次排序,这样就只要在有序数据上进行一次遍历就可以了。

image-20220316145334272

近似算法

精确贪心算法由于需要遍历所有的可能,非常消耗时间。并且当数据没有办法全部放进内存的时候,进行精准的贪心算法明显是不可行的,所以需要近似算法。精确贪心算法相当于,对于连续变量当中的所有分隔,都作为分割点的候选。一个很自然的近似算法就是,只从当中选择一个子集作为分割点的候选,就是将连续变量给映射到一个个的桶当中,然后基于这些通的统计信息,来选择分割点。具体算法如下图所示,只需要将每一个桶,作为其中的一个样本来思考就可以了。

image-20220316145351285

那么如何分桶实际上就是近似算法的关键所在,XGBoost的论文当中提出了两种方案:

  • 全局方法(global):即在最开始的构造时间就进行分桶,并且在整个节点分裂的过程当中,都采用最开始的分桶结果。

    • 需要进行更少的分桶操作
  • 局部方法(local):在每次分裂的时候,都重新进行分桶。

    • 每一次都在改进分桶方案,对于更深的树会更好
    • 需要更少的候选数量

当然从结果上来看,当全局方法的候选数量提升之后,也同样可以获取和局部方法差不多的表现。

image-20220320000350194

对于具体如何选择分割点,论文提出了一个叫做Weighted Quantile Sketch的方法。使用Dk={(x1k,h1),(x2k,h2),,(xnk,hn)}\mathcal{D}_k = \{(x_{1k}, h_1), (x_{2k}, h_2) ,\ldots, (x_{nk}, h_n)\}来表示第kk个特征以及样本的二阶梯度,定义一个rank函数为rk:R[0,+)r_k: \mathbb{R} \rightarrow [0, +\infty)如下所示:

rk(z)=1(x,h)Dkh(x,h)Dk,x<zhr_k(z) = \frac{1}{\sum_{(x, h) \in \mathcal{D}_k} h} \sum_{(x, h) \in \mathcal{D}_k, x<z}h

对于之前算法当中所提到的ϵ\epsilon,实际上就是要找到一系列的分割点{sk1,sk2,,skl}\{s_{k1}, s_{k_2}, \ldots , s_{kl}\},使得:

rk(sk,j)rk(sk,j+1)<ϵ|r_k({s_{k, j}}) - r_k (s_{k, j+1})| < \epsilon

所以ϵ\epsilon相当于一个度量采样点数量的值,ϵ\epsilon越小,对应的分割点就越多,数量近似为1/ϵ1 / \epsilon。 而采用二阶梯度作为分割依据的根据,来源于之前的目标函数:

L~(t)=i=1n[gift(xi)+12hift2(xi)]+Ω(ft)=i=1n12hi(ft(xi)gihi)2+Ω(ft)+constant\begin{aligned} \tilde{\mathcal{L}}^{(t)} &= \sum_{i=1}^n \left[ g_i f_t(\mathrm{x}_i) + \frac12 h_i f_t^2(\mathrm{x}_i) \right] + \Omega(f_t) \\ &= \sum_{i=1}^n \frac 12 h_i \left(f_t(\mathrm{x}_i) - \frac{g_i}{h_i}\right)^2 + \Omega(f_t) + \text{constant} \end{aligned}

之前的目标函数,实际上可以看作是一个以hih_i为权重的加权平方损失,所以采用hih_i来计算分割点。对于Weighted Quantile Sketch的具体实现,论文提供了一种新的数据结构,具体在论文附录当中,有兴趣的读者可以自己查看。

XGBoost对于稀疏特征有特殊的优化。稀疏矩阵的产生原因可能是缺失值,又或者One-Hot方法的使用。对于稀疏数据可以做一些特殊处理,我们可以将随意地分到左子树或者右子树,或者可以从数据当中来确定子树的分配方式。

image-20220316152549798

如上述算法所示,只对于对应特征没有缺失值的样本来考虑分割点,而对于所有缺失值,考虑统一分到左边或者统一分到右边。对于两种情况以及所有分割点,取当中能够获得最大增益的作为最终选择的节点分裂方式。

在B站上看到李沐在做AI方面论文的带读,看了一遍讲如何读论文,做个笔记,顺便记录一些自己的想法。

从整体上来看,整个视频基本上可以用下面这一个表来概括:

Pass1 Pass2 Pass3
Title
Abstruction
Introduction
Method
Experiment
Conclusion
  • Pass1
    • 只看标题、摘要和结论
    • 观察和自己的研究方向有没有关联性,适不适合自己的研究方向,确定论文值不值得读
  • Pass2
    • 快速过一遍整个论文,不需要了解所有的细节
    • 关注重要的图和表,看明白Method当中的流程图以及Experimet中图表的对应指标
    • 关注重要的相关工作,如果文章太难,可以先读引用的文献
  • Pass3
    • 关注所有细节
    • 换位思考,体会从提出问题到解决问题的全部过程
    • 寻找之后可能的改进空间

个人观点

李沐所讲的论文阅读方法,更多的是针对于经典论文,知道这是一篇好论文之后该如何去读。事实上,AI论文数量激增,大量论文并不能够提供三遍阅读的知识量。更多的是在第二遍阅读的过程当中,关注提出工作的启发点,以及论文当中是否出现类似数据泄露,对比不公平这样的情况。

PyTorch的Tensor支持非常多的索引方法,从Tensor当中取出一个子矩阵是一个常用的需求,如果是需要取出一个连续子矩阵或者子矩阵的索引是等间距排列的情况,可以直接采用切片索引的方式进行解决。对于更一般的情况,没有特别直接的解决办法。

为方便起见,这里定义数据以及需要取出的子矩阵的行列索引如下,这里设置的索引的行列编号相同。

1
2
3
4
5
6
7
8
9
10
11
>>> data = torch.arange(36).reshape(6, 6)
>>> data
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23],
[24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35]])
>>> idx = torch.LongTensor([1, 4, 5])
>>> idx
tensor([1, 4, 5])

如果直接利用索引进行取值操作,取到的是对角线上的元素

1
2
>>> data[idx, idx]
tensor([ 7, 28, 35])

如果按照先行后列的方法进行取值,可以获得预期的元素,如下所示

1
2
3
4
>>> data[idx][:, idx]
tensor([[ 7, 10, 11],
[25, 28, 29],
[31, 34, 35]])

但是这样取出来的Tensor并不对应原始矩阵当中的子矩阵,而是一个复制,如果在上面进行赋值操作,并不会对原始Tensor进行修改

1
2
3
4
5
>>> data[idx][:, idx] = 0
>>> data[idx][:, idx]
tensor([[ 7, 10, 11],
[25, 28, 29],
[31, 34, 35]])

如果有修改的需求,更加优雅的方式是采用np.ix_方法或torch.meshgrid方法。

np.ix_的示例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
>>> data[np.ix_(idx, idx)]
tensor([[ 7, 10, 11],
[25, 28, 29],
[31, 34, 35]])
>>> data[np.ix_(idx, idx)] = 0
>>> data
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 0, 8, 9, 0, 0],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23],
[24, 0, 26, 27, 0, 0],
[30, 0, 32, 33, 0, 0]])

torch.meshgrid的示例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> x, y = torch.meshgrid(idx, idx)
>>> data[x, y]
tensor([[ 7, 10, 11],
[25, 28, 29],
[31, 34, 35]])
>>> data[x, y] = 0
>>> data
tensor([[ 0, 1, 2, 3, 4, 5],
[ 6, 0, 8, 9, 0, 0],
[12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23],
[24, 0, 26, 27, 0, 0],
[30, 0, 32, 33, 0, 0]])

VSCode作为一个轻量级、跨平台的代码编辑器,在各种插件的支持下可以达到媲美IDE的编程开发体验。可以让人在一个编辑器中完成各种编程语言的开发工作,并且支持远程开发和协同开发。这里记录一些个人关于VSCode的基本设置,便于快速的在一台新的机器上配置好VSCode,并且满足基本的编程需求。相关的插件和配置列表可能会随着时间列表不断进行更新。

外观

字体

  • JetBrains Mono

    • 通过官方下载地址进行下载安装

    • setting.json 中添加

      1
      2
      3
      4
      {
      "editor.fontFamily": "JetBrains Mono",
      "terminal.integrated.fontFamily": "JetBrains Mono"
      }
  • Font Switcher

    • 拓展商店安装

颜色主题

  • One Dark Pro

    • 拓展商店安装
  • file-icons

    • 拓展商店安装
  • Bracket Pair Colorizer

    • 拓展商店安装
  • Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code

    • 拓展商店安装
    • 为VSCode页面提供中文支持
  • Better Comments

    • 拓展商店安装
    • 为注释提供更加丰富的颜色分类
  • Ruler

    • setting.json中添加

      1
      2
      3
      {
      "editor.rulers":[80]
      }
    • 在80个字符的位置提供一条标尺

远程开发

  • Remote -SSH
    • 拓展商店安装

编程相关

  • Code Runner
    • 拓展商店安装

Python

  • Python Extension Pack

    • 拓展商店安装
  • Mypy

    • 拓展商店安装
    • 为Python提供静态类型检查
  • Black

    • setting.json中添加

      1
      2
      3
      4
      {
      "python.formatting.provider": "black",
      "editor.formatOnSave": true,
      }
    • 为Python提供代码格式化

辅助编程

  • GitHub Copilot
    • 拓展商店安装
    • 需要绑定GitHub账号

其他

  • LeetCode
    • 拓展商店安装
  • Docker
    • 拓展商店安装
  • Kubernetes
    • 拓展商店安装

本文仅作为技术分享,希望大家都能够拥有锻炼的自由,但不鼓励使用技术进行跑步打卡

针对北大的校园体育课跑步打卡,已经有如PKUNoRun这样的开源项目,但是PKUNoRun仅仅支持安卓平台的PKURunner用户,使用如乐动力等其他软件作为打卡方式的并不能很方便的使用。

本文采取的思路为进行GPS定位模拟,从而实际生成跑步轨迹。这种方法的优缺点如下:

  • 优点
    • 操作简单,不需要针对APP进行分析和逆向
    • 通用,可以适用于各种打卡平台和系统
  • 缺点
    • 耗时,需要挂机等同于跑步本身的时间

网络上已有的大多数教程普遍基于Xcode进行实现,但是由于Xcode版本更新以及使用存在门槛,很多开源教程的方法存在过时的问题。这里采用开源工具LocationSimulator进行GPS模拟定位,比使用Xcode更加用户友好。关于LocationSimulator的具体安装以及使用可以参考GitHub上的项目主页,总体使用方法即使用数据线连接手机,选中已连接的设备,之后便可以通过GPX文件或者手动控制的方式进行GPS模拟了。

在完成软件安装之后,则需要确定对应的打卡地点的经纬度坐标,可以考虑直接在LocationSimulator结合手机地图软件进行标点,来确定对应位置的经纬度,也可以考虑使用google地图等别的方式。在操场跑道一圈标上足够多的点之后,可以考虑利用经纬度坐标加上轻微的随机化来生成跑步轨迹对应的GPX文件。

以北京大学五四操场作为打卡地点违例,在确定操场一圈的经纬度坐标之后,便可以采用以下代码进行GPX文件的生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import random

print("<?xml version=\"1.0\"?>\n<gpx version=\"1.1\" creator=\"Xcode\">\n")

points = [[39.98670783215012, 116.30664405734012],
[39.98618352533768, 116.30672865002087],
[39.98618352533768, 116.30672865002087],
[39.98581016760418, 116.30678898677019],
[39.98571908922798, 116.30689433671324],
[39.98561288688673, 116.30708452367374],
[39.98565674006142, 116.30742482197985],
[39.98570648307997, 116.30754884871082],
[39.98581008421271, 116.30760844737627],
[39.98630069330874, 116.30760921192963],
[39.98680940636073, 116.30751827280433],
[39.98691331279552, 116.30743465659333],
[39.98701527533597, 116.30723729348047],
[39.98702174059007, 116.30706010182425],
[39.98697385542131, 116.30689805739706],
[39.98687734879176, 116.30675655334059]]

for round in range(100):
for lat, lon in points:
print(" <wpt lat=\"{}\" lon=\"{}\"></wpt>\n".format(
lat + random.random() * 0.00005, lon + random.random() * 0.00005))

print("</gpx>")

之后直接运行Python脚本,将标准输出流重定向到文件,即可得到对应的GPX文件。

1
python gen_gpx.py > circle.gpx

在LocationSimulator通过File -> Open GPX File导入GPX文件,此时GPS坐标就已经在操场上进行规律的运动了。在这个时候打开手机上对应的打卡软件便可以足不出户进行跑步打卡了,同时通过在Walk和Cycle模式当中切换可以调节跑步速度,使得最终的平均速度在打卡范围之内。

P.S.

  1. 为保持LocationSimulator可以持续地对GPS定位进行修改,推荐保持电脑/手机屏幕常亮,避免锁屏。

  2. 由于连接有切断风险,所以LocationSimulator默认在断开链接之后仍然保持虚拟GPS,每次使用完记得点击左上角的X进行退出。

这是关于Optiver Trivia Night的记录(主要关于最开始的Quiz部分),最开始部分为一个大约30min的Quiz,之后为Optiver实习项目相关的介绍和答疑。其中Optiver是一家全球顶级的做市商,具体信息可以参见官网。

Quiz部分的得分与正确率和答题速度相关,在大家都答对题目的情况下,答题更快的可以获得更高的得分。

第一名可以获得Optiver的终面机会,前五名获得AirPods。

题目总量为四十道,总共用时30min,总共分为几类问题:

  • Quick Math (5s) - 基本的数学运算,难度大概在三位数加减法和两位数乘法的水平,重点可能在于时间限制较为紧迫
  • Fun Facts (20s) - 询问一些现实生活当中的数字,考察量级估计和现场搜索的能力
  • Find Pattern (30s) - 图形找规律,填写下一项是什么
  • Easy Brain Teasers (30s) - 较为简单,基本上是基本数学直觉的考察
  • Sequence (60s) - 数列找规律,填写下一项是什么
  • Medium Brain Teasers (60s) - 普通的概率问题
  • Hard Brain Teasers (120s) - 较复杂一些的概率问题

最后排名为Rank 25/150,感觉如果有一定的面试准备的话应该进前十问题不是很大。大部分题目并不会很难,提供足够的时间都能够做的出来,只是在Quiz的情况下会有很强的时间紧迫感。

以下是一部分的题目记录,其中由于Quick Math部分时间过于紧张,所以基本上没有保存到截图。

部分Fun Facts可能会随着时间改变,这里的结果以2021年9月作为参考


No. 7 (Hard Brain Teasers)

10个人围成一个圈。每个人可以拍一拍左侧或者右侧的人,请问被拍的人的个数的期望是多少?

  1. 2.5
  2. 5
  3. 7.5
  4. 8.36

Answer: C

No. 12 (Sequence)

2,3,7,16,65,?2,3,7,16,65,?

序列的下一个数字是什么?

  1. 321
  2. 81
  3. 122
  4. 179
Answer: A

No. 13 (Fun Facts)

以下最接近沪深市场一天的流通量的量级的是(以RMB计价)?

  1. 10b/day (一百亿)
  2. 100b/day (一千亿)
  3. 1t/day (一万亿)
  4. 10t/day (十万亿)

Answer: C

No. 14 (Sequence)

15,29,56,108,208,?15,29,56,108,208,?

序列的下一个数字是什么?

  1. 400
  2. 416
  3. 438
  4. 386
Answer: A

No. 15 (Fun Facts)

世界上最重的猪有多重

  1. 1157kg
  2. 1312kg
  3. 694kg
  4. 866kg
Answer: A

No.16 (Hard Brain Teasers)

有5封不同的信来自5个不同的人。如果我们随机分发信件,没有一个人收到Ta自己的信的概率是多少?

  1. 4/5
  2. 11/30
  3. 7/20
  4. 5/12

Answer: B

No. 17 (Medium Brain Teasers)

地上有一个10cm * 10cm的无限大网格,你随机扔一枚直径为1cm的硬币。问硬币碰到网格线上的概率是多少?

  1. 50%
  2. 25%
  3. 19%
  4. 11%

Answer: C

No. 18 (Find Pattern)

image-20211002152158768

Answer: A

No. 19 (Medium Brain Teasers)

红色柱体的高度是多少?

image-20211002152250669

  1. 8
  2. 10
  3. 20/3
  4. 15
Answer: A

No. 21 (Easy Brain Teasers)

下列的哪一个不是素数?

  1. 257
  2. 289
  3. 157
  4. 349
Answer: B

No. 22 (Medium Brain Teasers)

你有机会可以对数字1~6进行下注,投掷三个骰子。如果你选择的数字出现一次,你获得$1;如果两次,你获得$2;如果三次,你获得$3。你所获得金额的期望是多少?

  1. $0
  2. $0.5
  3. $1
  4. $1/3

Answer: B

No. 23 (Fun Facts)

哪一个整数区间当中包含最多的素数?

  1. [1, 100]
  2. [101, 200]
  3. [201, 300]
  4. [301, 400]

Answer: A

No. 24 (Hard Brain Teasers)

(1+2)3000(1+\sqrt{2})^{3000}小数点后第100100位的数字是多少?

提示:(1+2)3000+(12)3000(1+\sqrt{2})^{3000} + (1-\sqrt{2})^{3000} 是一个整数

  1. 0
  2. 1
  3. 4
  4. 9

Answer: D

No. 25 (Medium Brain Teasers)

你站在地球的表面上,你往南走1英里,往西走1英里,往北走1英里,最后你停留在了你开始的地方。地球上有多少点可以让你完成以上的行为?

  1. 0
  2. 1
  3. 2
  4. 无穷多个

Answer: D

No. 26 (Sequence)

91,85,94,83,97,81,?,7991,85,94,83,97,81,?,79

序列的下一个数字是什么?

  1. 80
  2. 100
  3. 102
  4. 95

Answer: B

No. 27 (Hard Brain Teasers)

考虑一个平面上的5个点,在所有的点之间两两连线,你拥有了x条直线。那么以下哪个不可能是x的值?

  1. 5
  2. 6
  3. 7
  4. 8

Answer: C

No. 28 (Easy Brain Teasers)

一个立方体的面、棱、顶点数的总和是多少?

  1. 26
  2. 28
  3. 30
  4. 24
Answer: A

No. 29 (Sequence)

99,18,36,9,1899,18,36,9,18

序列的下一个数字是什么?

  1. 36
  2. 27
  3. 9
  4. 3

Answer: C

No. 31 (Fun Facts)

一个标准的奥林匹克游泳池(25m * 50m * 2m) 有多少升水?

  1. 250
  2. 25000
  3. 2500000
  4. 250000000
Answer: C

No. 34 (Hard Brain Teasers)

一个密码只能以字母而不能以数字开头,那么po88p881有多少种排列可以是密码?

  1. 315
  2. 420
  3. 525
  4. 840
Answer: A

No. 35 (Fun Facts)

火星的平均温度是多少?

  1. -100°C
  2. -60°C
  3. -20°C
  4. 20°C
Answer: B

No. 36 (Medium Brain Teasers)

考虑一个时钟,一天当中时针和分针会重合多少次?

  1. 22
  2. 23
  3. 24
  4. 26

Answer: A

No. 37 (Easy Brain Teasers)

如果下列命题有正确的话,哪一个命题是正确的?

  1. 在列表中至少有1个命题是错误的
  2. 在列表中至少有2个命题是错误的
  3. 在列表中至少有3个命题是错误的
  4. 在列表中至少有4个命题是错误的
Answer: A

No. 38 (Sequence)

3,3,27,69,129,?-3,3,27,69,129,?

序列的下一个数字是什么?

  1. 178
  2. 207
  3. 312
  4. 268
Answer: B

No. 39 (Find Pattern)

image-20211002153239630

Answer: B

No. 40 (Hard Brain Teasers)

一个国际象棋棋盘的每个小正方形边长都为2。请问在棋盘上作一个圆,能够使得整个圆的圆周都在黑色方形内部的最大的圆半径为多少?

  1. Sqrt(2)
  2. 2
  3. Sqrt(10)
  4. Sqrt(34)

Answer: C

所谓的72规则是一个用来在已知年化收益的情况下,估计投入资金实现翻倍需要年数的估算公式,公式表示如下:

Years to Double=72Interest Rate\text{Years to Double} = \frac{72}{\text{Interest Rate}}

首先通过homebrew安装rustup管理工具:

1
$ brew install rustup

安装完后发现并不能够找到rustup指令,通过brew list进行查询,发现实际上安装的为rustup-init,于是再在命令行执行:

1
$ rustup-init

顺着流程安装完成之后,重启终端便可以安装好rust环境以及相关的工具链。

可以查看对应rustc以及cargo的版本:

1
2
$ rustc --version
rustc 1.53.0 (53cb7b09b 2021-06-17)
1
2
$ cargo --version
cargo 1.53.0 (4369396ce 2021-04-27)

简单的创建一个rust语言版的Hello World进行测试:

1
2
3
fn main() {
println!("Hello World!");
}

将其保存为hello.rs

在命令行使用rustc将其编译为可执行文件:

1
$ rustc hello.rs

之后直接执行可执行文件,便可以在终端看到对应的输出了!

1
2
$ ./hello
Hello World!

Bob和Alice被邀请参加一个卡牌游戏。庄家从一组扑克牌(4种花色,A-K)给Bob随机发放5张。Bob可以选择其中4张给Alice看,然后Alice被要求猜测Bob手中剩下的一张牌的花色和点数。

问:

Bob和Alice能否仅仅根据牌的花色和点数设计出一个策略,使得无论Bob拿到的是哪五张牌,Alice都能成功猜测出最后一张牌的花色和点数。

13=23!+113 = 2 * 3! + 1

有人直接给出了这么一个式子,感觉思考思考还是很有意思的。

首先两个重要前提,一个在于Bob可以自由选择翻牌的顺序,另一个就是可以选择让Alice猜哪一张牌。

而确定一张牌需要花色加上点数。

首先是花色,花色由于抽屉原理,一定会有两张相同花色的牌。那么可以将一张牌当作被猜的牌,另一张被第一个翻出,当作花色指示牌。

对于点数而言,剩下三张牌可以考虑预先对于花色和点数的组合大小进行一个定义,那么三张不同的牌一共涉及3!=63!=6种排列方式,而1313张牌A-K排成一个圆周,每一张牌到剩下十二张牌的距离都在66以内(顺时针或逆时针)。于是可以预先定义顺/逆时针,然后以第一张标示牌沿顺/逆时针走kk格。kk为之后三张牌组合出来对应的数字,由于Bob拥有一定的自主选择权,可以通过对调第一张牌和最后一张牌的方式来确保一定是可达的。

此时再回头看最开始的那个式子,只用一个公式描述清楚了整个状态,非常精妙!

涉及时间序列的数据当中常常会碰到需要将其转换成一个个滑动窗口来构造对应训练数据的场景,PyTorch里面提供了torch.Tensor.unfold()方法可以直接完成操作。

接受的三个参数依次如下:

  • dimension (int) – unfold操作所作用的维度
  • size (int) – 滑动窗口的窗口大小
  • step (int) – 滑动窗口的步长

以下为使用的样例:

1
2
3
4
5
6
7
8
9
10
>>> x = torch.arange(1., 8)
>>> x
tensor([ 1., 2., 3., 4., 5., 6., 7.])
>>> x.unfold(0, 2, 1)
tensor([[ 1., 2.],
[ 2., 3.],
[ 3., 4.],
[ 4., 5.],
[ 5., 6.],
[ 6., 7.]])