# README

这里是清雨影的Blog。

对于我个人来说，是一份技术备忘录，对于读者来说，可以是随时查阅的工具站点。

我工作之余写各种开源软件，希望能给这个世界带来哪怕一点点的便利或者贡献，这个博客的目的也是一样的。

如果这个博客对你有帮助，或者你发现了错误，欢迎随时通过邮箱 nigel434ATgmail.com (请将AT替换为@，这么做是为了防止垃圾邮件) 联系我。

我很乐于看到我写的东西对读者有帮助，仅这一条就可以构成我写作的原始动力了。

## 关于转载

你可以转载任何你想转载的文章，但是请带上原地址。

## 源码

这个博客的源代码在：[TsingJyujing/blogs](https://github.com/TsingJyujing/blogs)。

目前不支持RSS订阅，但是你可以关注这个Github项目Commit的RSS。


# 有限单元法

不要问为什么一个机器学习工程师要搞这个，问就是为了爱。

## 主要学习资料

### 视频课程

* Bilibili：[清华大学—有限元分析及应用](https://www.bilibili.com/video/av25035770)
  * B站小伙伴热心制作PPT讲义：链接:<https://pan.baidu.com/s/1x0NSXVVmt6u1-0dvAqIfgw> 提取码:s719
* 网易公开课：[清华大学有限元分析及应用 13讲](https://open.163.com/newview/movie/free?pid=WFTMARPBG)
  * 视频顺序有问题
* 百度云盘（蜗速下载）：[023.清华大学—有限元分析及应用（1.19 GB）](https://pan.baidu.com/s/1pJn0hcR)

以上视频都是一样的，选一个方便的用即可。

### 课程概要

整个课程有第15讲，分为三个部分：

第一部分主要为基本原理，包含第1讲到第6讲。

第1讲为引论，介绍力学的分类、变形体力学的要点、微分方程求解的方法、关于函数逼近的方式、针对复杂几何域上的函数表征及逼近、针对复杂几何域的分片函数逼近、有限元发展的历史和软件；

第2讲为基于直接刚度法的杆系有限元方法，内容有：弹簧的力学分析原理、弹簧单元与杆单元的比较、杆单元的坐标变换、杆结构的ANSYS实例分析；

第3讲及第4讲是针对复杂几何形状变形体的力学描述，内容有：力学描述的基本思路、平面问题三大方程构建、两类边界条件、平面纯弯梁的描述及求解、空间弹性问题的完整描述、关于张量的描述及理解；

第5讲是变形体力学方程求解的试函数方法的原理，内容有：平面弯曲梁求解的试函数方法、变分方法、能量原理；

第6讲是基于试函数方法的经典实现及有限元实现，内容有：基于试函数的经典方法与有限元方法、自然离散与逼近离散、有限元方法中的基本步骤。

第二部分主要为有限元方法，包含第7讲到第13讲。

第7讲是杆、梁结构的有限元分析，内容有：杆单元构建及MATLAB编程、梁单元构建及MATLAB编程、分布力的处理、杆梁结构的ANSYS实例分析；

第8讲及第9讲是连续体结构的有限元分析，内容有：平面3节点三角形单元及MATLAB编程、平面4节点矩形单元及MATLAB编程、轴对称单元、分布力的处理、空间4节点四面体单元、空间8节点正八面体单元、参数单元的原理、典型空间问题分析的ANSYS实例；

第10讲是 有限元方法中的基本性质，内容有：节点编号与存储带宽、形状函数矩阵与刚度矩阵的性质、边界条件的处理与支反力的计算、位移函数构造与收敛性要求、C0单 元与C1单元、单元的拼片试验、有限元分析数值解的精度与性质、单元应力计算结果的误差与平均处理、控制误差和提高精度的h方法和p方法；

第11讲是高阶及复杂单元，内容有：1D高阶单元、2D高阶单元、3D高阶单元、基于薄板理论的弯曲板单元、子结构与超级单元；

第12讲及第13讲是有限元分析的应用领域引论，内容有：结构振动的有限元分析、弹塑性问题的有限元分析、传热问题的有限元分析、热应力问题的有限元分析。

第三部分主要为有限元建模，包含第14讲及第15讲。

第14讲是有限元分析的典型PROJECT；

第15讲是自主专题分析CASE STUDY，要求学生独立完成一个有限元分析实例。

### 其余材料

* [曾老师自己做的讲义](https://www.academia.edu/34293909/%E6%9C%89%E9%99%90%E5%85%83%E5%88%86%E6%9E%90%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B_Fundamentals_of_Finite_Element_Analysis)

## 其余资料收集

(慢慢补充)

* [有限元方法编程](http://cstam.org.cn/static/old/upload/fckeditor/%E6%9C%89%E9%99%90%E5%85%83%E6%96%B9%E6%B3%95%E7%BC%96%E7%A8%8B%20%20%EF%BC%88%E7%AC%AC%E4%B8%89%E7%89%88%EF%BC%89.pdf)


# 1. 引论

## 有限的单元，无限的能力

基本就是说说有什么用，不过需要吐槽一下，那些视频看上去很炫酷，但是在没学过FEM的人看来就平平无奇，也不知道干什么用的。

## 引论

### 1.1 力学的分类：质点、刚体、变形体的力学

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8692b136dcd681f7b3e956f0753284c2cd308f60%2Fvlcsnap-2021-05-03-00h10m55s197.png?alt=media)

以卫星为例子，描述轨道只要简化为质点就可以，使用三个坐标就可以标示，如果描述姿态还需要再加3个坐标，如果考虑电池板等柔性元件就更加复杂。

想起一个大佬说的，做有限元分析，不是考虑加什么东西，而是考虑在不影响结果的情况下，可以从现实世界的模型里减去什么。

下面更加具体的分析：

* 质点力学：初中学过的
* 刚体力学（理论力学）：物体形状不变，但是质量分布不能简化为一个点
* 变形体的力学
  * 简单形状
    * 简单形状，简单组合：材料力学，小变形，形状简单
    * 简单形状，复杂组合：结构力学，各种复杂桁架的受力分析（土木应该会经常用吧）
    * 复杂形状：弹塑性力学，需要使用微元法分析了，预感这是这门课解决的问题，未必能用解析法解析了。

### 1.2 变形体力学的要点

变形体力学的要点，曾老师设计了三个同种材料单拉实验，三个实验的受力-位移曲线都不一样，引导出描述材料中的不变量：弹性模量E的定义：

$$E = \frac{\sigma}{\epsilon}$$

其中：

* $$\sigma = F/A$$
* $$\epsilon=u/L$$
* F 为试样受拉力
* A 为试样横截面积
* u 为受拉力F以后，试样的形变量
* L 为试样长度

### 1.3 微分方程求解的方法

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e7ac81df4c52460352484d7317f73dcf4699e701%2Fvlcsnap-2021-05-03-00h31m08s058.png?alt=media)

这是我踩的第一个坑，我想了10分钟才明白为什么是u的二阶导数。这里并不是位移-速度-加速度。 首先视频里说“长度上受到均布载荷P”，我理所应当的认为P是压强，应变也就是F/A，其实不是。

我们如果在自由端施加F的拉力，的确小杆子内部的应变处处相等，但是这样的简单载荷是不需要微分方程求解的，所以给定了一个复杂的情况，这里的P的单位其实是N/m，杆子内部的受力不均匀，是一个从左到右逐渐加码的载荷。

所以，应变的大小其实是 $$\epsilon(x) = \frac{du}{dx}$$，那么应力就是 $$\sigma(x) = E \epsilon(x)$$，截面受力 $$F(x) = EA \epsilon(x)$$ 考虑到P是均匀加负荷，所以F的导数等于P，也就是力从0开始逐渐变大到LP（从右到左看的话）。

最后我们得到：

* $$\frac{dF(x)}{dx}=-p$$
* 整理一下：$$\frac{d\frac{du}{dx}EA}{dx}+p=0$$
* 再整理一下： $$\frac{d^2u}{dx^2}+\frac{p}{EA}=0$$

得到方程以后的事情就比较顺理成章了，这个微分方程但凡学过高数都会解，唯一的问题是，两次积分会产生两个常数项，第一次产生常数项的时候，要用第二个约束条件带入得到 $$C\_1=\frac{PL}{AE}$$，第二个常数项要用第一个约束条件带入得到 $$C\_2=0$$

接下来讲了两个近似方法，第一个是差分，其实就是折线法（记忆模糊了，应该没错）求解微分方程近似解，说到这个我就兴奋了，除此之外，其实还有龙格库塔之类的一票方法。

老师又说了一个试函数法，看到待定系数和残差方程我就更加兴奋了，你这个是要搞最优化？要不要来个万能函数搞机器学习？

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f6823730273de0420c84d02ba2ad6b16076fe19d%2F%E4%BD%A0%E8%81%8A%E8%BF%99%E4%B8%AA%E6%88%91%E5%8F%AF%E4%B8%8D%E5%9B%B0%E4%BA%86.gif?alt=media)

但是下面的步骤开始匪夷所思：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-92892391718582298f133556d8fe3ece82400d0b%2Fvlcsnap-2021-05-03-01h13m21s880.png?alt=media)

一般这种用一个（组）函数的线性和逼近另一个函数，一般考虑最小二乘法，这里把一个函数和另一个函数乘起来积分等于零（而且还不要求试函数组正交），用这种方法求系数的我还没见过。

经过了解，这个方法叫Galerkin（伽辽金）加权残值法（[Wiki 伽辽金法](https://zh.wikipedia.org/wiki/%E4%BC%BD%E8%BE%BD%E9%87%91%E6%B3%95)），简单地说，就是用试函数的基函数作为权重函数，就能取得系数。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3a347267103d227342e568842ccb574eedf93808%2F2021-05-03-01-15-32.png?alt=media)

我参考了[这个文章](https://zhuanlan.zhihu.com/p/33216023)，但是他也没有细说原理，这个应该不重要，如果以后需要再来补充吧。

### 1.4 关于函数逼近的方式

除了试函数法，还可将函数分成多个小片，逐片拟合。

### 1.5 针对复杂几何域上的函数表征及逼近

主要就是遇到形状复杂的形状，只能采用分块的方式来做。试函数在这里基本就是废物……

### 1.6 有限元的核心：针对复杂几何域的分片函数逼近

这里开始涉及到有限元的核心，单元分割，这里举了各种各样的单元，但是实际上不止老师列举的单元，有一个[FEMTable](http://www-users.math.umn.edu/~arnold/femtable/)的网站专门记录了应该是所有的有限单元的形式。

### 1.7 有限元发展的历史和软件

略，不过视频出现了一个梗，老师举例了几位FEM的开创者都很长寿，得出了研究有限元可以长寿的结论。 但是曾老师自己享年56岁……老师你为什么要乱立Flag啊哭……


# 2. 基于直接刚度法的杆系有限元方法

## 2.1 弹簧的力学分析原理

首先是大家都知道的胡克定律： $$F=k\delta$$。 但是这里引入了一个新的概念：刚度方程/矩阵。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-cf754424d27e394e9767c548d0901c3b14bffce9%2Fvlcsnap-2021-05-03-15h09m52s741.png?alt=media)

乍一看是画蛇添足之举，实则另有用处，这么处理之后的单元规范了，就可以和其他杆件连接了。

下一步分析两个弹簧的连接+存在一个外力$$F\_2$$作用在节点2上的实例：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-80ceb6ce4b54f0e93be99f55326e7967d07f13ea%2Fvlcsnap-2021-05-03-15h14m51s011.png?alt=media)

建模过程视频很详细，略了，大致思路是：$$F\_2$$ 给右边的弹簧，弹簧内力设置为$$F\_{I\_2}$$，左右相互抵消，这个$$F\_{I\_2}$$将来要在方程组中约去。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-04a55ad0f36eab629f8768ed30e4d4ffaa458c3d%2Fvlcsnap-2021-05-03-19h42m04s254.png?alt=media)

这里扩充矩阵，使其标准化为3✖3的矩阵，方便相加，这里取了一个巧，正常我们有两个方程组，例如：$$A\_1x=b\_1$$和$$A\_2x=b\_2$$，要揉成一个，一般会取两个系数，比如$$\alpha\_1$$和$$\alpha\_2$$，加上系数以后相加：

$$(\alpha\_1A\_1 + \alpha\_2A\_2)x=(\alpha\_1b\_1 + \alpha\_2b\_2)$$

然后调整这些系数保证$$F\_{I\_2}$$约去，视频里直接让两个方程组b的第二项的内力互为相反数，这样直接加起来就约掉了。

最后考虑$$u\_1$$和$$u\_3$$都是0（约束条件），所以得到u2的计算公式：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-dfb6f4984d6f14eac91bd2fe79308ae07d371495%2Fvlcsnap-2021-05-03-19h50m13s064.png?alt=media)

## 2.2 弹簧单元与杆单元的比较

杆单元和弹簧一样，都是线性元件，所以计算方式也是一样的。考虑弹性模量为E，长度为l，截面面积A的杆，等效于 $$k = \frac{EA}{l}$$ 的弹簧。

Q.E.D.

## 2.3 杆单元的坐标变换

之前的杆单元的位移u都是沿着杆的方向定义的，但是这个时候如果出现斜拉的杆件，计算就会显得有点复杂。

2.3节的主要思想，就是统一坐标系，全部分解到x-y或者x-y-z坐标系，这样刚度矩阵加起来就更加的流畅。

这一切，都会导致计算量大幅上升，但是这是值得的，因为带来的好处就是计算更加规范了，这样就利于编程实现，反正最后受苦的是CPU/GPU又不是我。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-1079a2cb1c0de5f367e7304e0736b699de0238b8%2Fvlcsnap-2021-05-03-21h33m39s221.png?alt=media)

这里就很清晰的展示了坐标变换的方式，虽然2个坐标变4个了，但是考虑小变形问题的话，$$\alpha$$ 是基本不会变化的，计算中省略其变化，所以秩其实还是2。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-42251bdc9d82c2656af5187824fa579ca4cb4d72%2Fvlcsnap-2021-05-03-21h35m54s041.png?alt=media)

这里我们进行矩阵化，我们用矩阵T联系了变化前和变化后的坐标。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6973cdca608b5b383ecccb7d2ac7a328961aa31e%2Fvlcsnap-2021-05-03-21h36m26s415.png?alt=media)

我们也需要对刚度矩阵进行同等的变换才可以让刚度阵（其实还有F向量）适应新的坐标，直接思考刚度阵的变化是很费脑细胞的，所以这里使用（线性）代数进行等效变换，我们得到了新的刚度矩阵，过程如下：

* 首先把坐标$$\bold{q}^e$$替换成$$\bold{T}^e\bold{\overline{q}}^e$$
* 两边左侧乘以矩阵$$\bold{T}^{eT}$$。
  * 其实根据线性代数的理论这里其实乘什么都行，但是由于要对力F也进行坐标变换才乘以的这个矩阵。
  * 只有用$$\bold{T}^{eT}$$，右侧的式子才有物理意义了，代表的是力。
* 最后根据线性代数的结合律，我们把$$\bold{T}^{eT}\bold{K}^e\bold{T}^e$$捆绑一下，就得到了新的刚度矩阵。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-dcda4ecab9c4e17ac599784ac226bee0618d7b3a%2Fvlcsnap-2021-05-03-21h50m37s065.png?alt=media)

老师还列举了三维的形式，三维的刚度矩阵写出来可能一张PPT就满了（6x6），所以就略了。

## 2.4 一个四杆结构的实例分析

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a04cf20e0d43c227992ca1da85ff5511b5520c2f%2Fvlcsnap-2021-05-03-23h29m03s501.png?alt=media)

以上是今天要分析的问题，四个线性元件组成的桁架结构的受力分析。第一件事情其实是体力活儿，列出所有杆和节点的信息：

| 杆子 | 节点    | l   | $$n\_x$$ | $$n\_y$$ |
| -- | ----- | --- | -------- | -------- |
| 1  | {1,2} | 400 | 1        | 0        |
| 2  | {3,2} | 300 | 0        | -1       |
| 3  | {1,3} | 500 | 0.8      | 0.6      |
| 4  | {4,3} | 400 | 1        | 0        |

| 节点 | x   | y   |
| -- | --- | --- |
| 1  | 0   | 0   |
| 2  | 400 | 0   |
| 3  | 400 | 300 |
| 4  | 0   | 300 |

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-da892bbc162bbfba2f2eaf369bbc1a8769463bb2%2Fvlcsnap-2021-05-03-23h38m14s659.png?alt=media)

随后就是看着图片和表格填刚度矩阵，这个活太细碎了……

曾老师是先填写了一个个小的矩阵，然后扩充填0以后相加，不过我觉得直接填写应该也OK。

这里的支反力是因为有了约束条件，当你约束一个坐标的时候，坐标是已知了，但相应的你就产生一个未知的力。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-aa87b75dc0a67cda725356b97aa1a58921edabc5%2Fvlcsnap-2021-05-03-23h38m40s776.png?alt=media)

这就是刚度矩阵，好特么大……（对于人类而言），其中的$$F\_x2$$，$$F\_x3$$，$$F\_y3$$都是刚才已知的力。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-57b02a9e2dab6f94a83e1d9026847271732f63c4%2Fvlcsnap-2021-05-03-23h40m00s293.png?alt=media)

考虑到$$u\_1$$，$$v\_1$$，$$v\_2$$，$$u\_4$$，$$v\_4$$全都是0，所以未知数只有3个，而且$$u\_2$$直接就可以肉眼算出，最后只剩下一个二元线性方程组。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bd160b4c8ba1fe50385959d89fadc0e701adcb6f%2Fvlcsnap-2021-05-03-23h40m13s974.png?alt=media)

节点位移求出以后，支反力也就知道了，这个是很自然的，因为位移知道以后，拉伸量也就知道了。

## 2.5 四杆结构的ANSYS实例分析

略


# 3. 针对复杂几何形状变形体的力学描述（1）

## 前言

* 3.1 力学描述的基本思路及关于变形体材料的基本假设
* 3.2 指标记法
* 3.3 关于三大变量及三大方程的思路

这一讲主要是针对复杂形状的弹性体进行建模，这里的例子用的是二维平面，为了单位的合理所以强行加了一个厚度t，在之后的计算中基本都被约掉了。 不考虑三维可以理解，毕竟阐述建模思想就可以了，三维的话光记住所有的应力就够费脑子了。

首先前面的部分主要是回顾和引入，从这里开始：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-40f9b147e0dfd7d4e82d2a0d7549046dba3c39bc%2Fvlcsnap-2021-05-04-17h22m58s007.png?alt=media)

其实大多数的材料都不完全满足这五个特性。 就比较熟悉的钢铁来说，铸造的物体可能有沙眼气泡，锻造的物体根据锻造的时候的受压情况，并不是各向同性的，焊接的还会有内应力，但是计算机要的就是简化。

然后针对确认不满足这五个情况的其实有一些其他的理论解决，但是这些不在讨论之列。

指标记法主要是对于如何缩写一些求和表达式的约定。 这里有一小半时间都在介绍指标记法和Voigt，看上去写的是简单了，但是约定一大堆，我个人觉得至少不利于我的学习和理解。

建模的思路很简单，老师总结为三大变量（应力、应变、位移），三大方程（力平衡，变形协调，物理（广义胡克定律）），约束。

从顺序上理解的话：

* （切割）把复杂零件切割为小方块（边界上是小三角块）
* （应力）针对小方块的受力情况，构建力的平衡方程。
* （应变）针对变形，施加约束条件，使得变形不能撕裂或者重叠
* （应力和应变关系）考虑弹性模量和泊松比，构建应力和应变的关系
* （边界）针对边界，考虑两种情况，力约束和位移约束
  * 位移约束就是控制位移，没毛病
  * 力约束的话针对小三角块构建力的平衡方程

## 3.4 平面问题的平衡方程构建

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d32ef16cb1c244591279be057c484ffbf2c716fa%2Fvlcsnap-2021-05-04-23h09m16s927.png?alt=media)

平衡方程主要考虑：x方向，y方向，力矩。

物理上理解，如果这个微元受力不平衡，就会产生加速度或者角加速度，最后就是飞起来或者转起来。

做静态分析的时候，这明显是不靠谱的，零件会飞走的，所以应该是合力为0。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-db06cc67f61c0cb1872f0381abab9b7feb8013bc%2Fvlcsnap-2021-05-05-00h26m32s410.png?alt=media)

分析的时候会有一个问题，按道理说，左右两边（或者上下）的应力应该是相等的，但是并不是，相等意味着导数为0，意味着受力完全均匀分布，这个明显不符合实际情况。

所以我们考虑在这个地方用泰勒展开，然后用一阶项去近似这个增量。

在x方向除了有正应力（的差分），还有剪切应力（的差分），上下面受剪切应力不一样的时候，也会产生x方向的力，但是由于上下面的差分，所以这里对y求偏导。

最后的最后，我们发现 $$dxdyt$$ 作为公共项约掉了，最后考虑一下和体积力（重力/电磁力等）平衡即可。 y方向也如法炮制。

最后是剪切应力，思路是类似的，但是结论稍有不同，最后得到 $$\tau\_{xy}=\tau\_{yx}$$，这个其实也不难理解，正应力抵消了，又没有“体积扭矩”这种东西，如果剪切应力不一样的话单元直接得转起来了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ed0779dfbd5587da2ab373f0d79b1efdabe607dd%2Fvlcsnap-2021-05-05-16h42m12s722.png?alt=media)

最后看一下结论：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-44a24d5e7350d6fa609cb3c9b2901ce0167f3de6%2Fvlcsnap-2021-05-05-16h44m19s386.png?alt=media)

结论还是简洁的，一言以蔽之，应力抵消体积力。

## 3.5 平面问题的几何方程构建

对于x和y来说，应变即位移的偏导数，这个哪怕没有几何图形也可以直观理解。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ba4ad6f4745e47e895e23e218423f9fdefa41fa5%2Fvlcsnap-2021-05-05-17h30m37s400.png?alt=media)

对于剪切力引起的应变，采用$$\alpha + \beta$$的方式定义，也就是图里变形以后的尖角和90度的差值。

需要注意的是，这里的角度都是用$$tan(\alpha) + tan(\beta)$$代替的，主要还是因为角度太小了，可以不考虑非线性的tan函数。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6676f0c5d506327474b7830c442f97fd84a5e976%2Fvlcsnap-2021-05-05-17h31m02s197.png?alt=media)

这里是几何方程和指标记法，说实话，约定b我觉得牵强了点……

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f0a36a33d146d6c84d8cdc3bca6d6cabb098a58b%2Fvlcsnap-2021-05-05-17h31m17s929.png?alt=media)

满足[变形协调条件](https://en.wikipedia.org/wiki/Compatibility_%28mechanics%29)才能说这个位移场是一个物理上有效的位移场，因为在这里的分析中，材料不会撕裂，更不会重叠，一定是连续的。

说实话我没法从直观上理解这个方程，只能从数学上理解，如果每一个单元都按照第一章图画的那样变形（矩形变菱形），我们可以从位移推导出三个应变量的公式，但是2个数值映射到3个数值，一定有一个数值是多余的，这个就是切应变（当然也可以是其它的，只不过我觉得切应变孤单好欺负）。

于是可以从数学上，构造出正应变和切应变在二阶偏导数上的关系。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a5add28bfe019a1919dafc4285e6a0cf95141965%2Fvlcsnap-2021-05-05-17h31m38s163.png?alt=media)

## 3.6 平面问题的物理方程构建

学材料力学的时候，我们知道除了杨氏（弹性）模量，还有一个泊松比，正泊松比的材料越拉越窄（这个符合常识），负泊松比的材料越拉越宽。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9f9f6f07fa7670d3acadab8f327db1fd980ef9a1%2Fvlcsnap-2021-05-05-18h00m28s820.png?alt=media)

这里就开始把应力和应变串到一起了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6c7e26dcb44a8f07e8212cdd5c24d6b673815d7b%2Fvlcsnap-2021-05-05-18h00m49s050.png?alt=media)

多出来的剪切模量可以用泊松比和弹性模量求出，最后我们就可以求得应力和应变的互换公式（材料需要指定）。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-1b2f4397c0f428922d96b643a2dd95b7a6dd7bd2%2Fvlcsnap-2021-05-05-18h04m10s106.png?alt=media)

后面指标记法和Voigt规则的部分略。

## 3.7 两类边界条件

边界是特殊的单元，其实边界有两种，受力（受力为0算特殊的受力）和（位置）固定。

这里用偏导符号表示物体的边界（三维则是封闭曲面），受力约束和位置约束需要不重不漏的覆盖所有边界。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-1cb3931713786b7d388367ddb145959fd1a60a61%2Fvlcsnap-2021-05-05-18h13m11s330.png?alt=media)

针对三角形的小单元构建力和力矩的平衡方程，需要考虑斜边的方向余弦。

最后得出结论，除了剪应力需要相等以外，剪应力加正应力需要抵消相应方向上的外力。考虑到外力为0其实是挺常见的边界条件，所以这里我们可以看到剪应力和正应力的互相转换。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-234e4048d4650f9399a3cd78c3707738fb2303d6%2Fvlcsnap-2021-05-05-18h13m30s842.png?alt=media) ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-890f6b3f4afde245e8771a99d748e9a43a8ebb95%2Fvlcsnap-2021-05-05-18h13m40s932.png?alt=media)


# 高级物理引擎实战指南笔记

这里是[高级物理引擎实战指南](https://github.com/taichi-dev/games201) 的课程笔记。


# 专题：线性方程组求解

## 简介

这里完整介绍一些求 $$\bold{A}\vec{x}=\vec{b}$$ 的方法，特点是A矩阵的规模比较大。 正常来说，求解线性方程组可以通过求逆矩阵，高斯消元，克拉默法则（~~大学线代第一节课就学这个没用的东西，不会真的有人用吧，不会吧不会吧~~）等等方式去求解。 但是A很大的时候这个方法就失效了，这个时候一般用迭代法，本文以介绍迭代法为主。

这里解的方程组一定要正定，负定的矩阵解起来注定是失稳的。如果是正半定，或者说奇异矩阵（存在为0的特征值），那么解将会在某个子空间里而不唯一。

其实从二次型的视角就很容易看出：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ee3740d5792d0d6aabeb0c479ccee05e4cfd5a1e%2F2020-07-24-23-17-16.png?alt=media)

（请注意，本文中的推导仅仅是验证实验用，性能并非最优）

## Jacobi 迭代与 Gauss-Seidel

### Jacobi 迭代

推导过程：

* 我们把A矩阵分尸三块：$$A = D - L - U$$
  * 其中，D是对角线，L是上三角取负，U是下三角取负。
* 这样，$$Ax=b$$ 就可以写成 $$(D-L-U)x=b$$
* 移动一下：$$Dx=(L+U)x + b$$
* 由于D是对角矩阵，可无成本求逆：$$x=D^{-1}(L+U)x+D^{-1}b$$

于是我们利用下列公式迭代更新：

$$
x^{(k+1)} = D^{-1}(L+U)x^{(k)} + D^{-1}b
$$

即可求得解，至于为什么是这样，可看收敛性分析。

%accordion% Numpy实现 %accordion%

```python
def jacobi_iteration(A,b,init_x,iters=10):
    dim = init_x.shape[0]
    x = init_x
    result = np.zeros((iters+1,dim))
    result[0,:] = x.reshape(-1)

    diag_A = np.diag(A)
    D_inv = np.diag(1.0/diag_A)
    LU = np.diag(diag_A) - A
    B = D_inv @ LU
    D_inv_b = D_inv @ b

    for i in range(iters):
        x = B @ x + D_inv_b
        result[i+1,:] = x.reshape(-1)
    return result
```

%/accordion%

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-575622a004463bad70e76e563eb1f8a5e1fa2409%2F2020-07-25-12-55-28.png?alt=media)

### Gauss-Seidel

Jacobi迭代中，$$x^{(k+1)} = D^{-1}(L+U)x^{(k)} + D^{-1}b$$， 这里的 $$x^{(k+1)}$$是向量，Jacobi迭代更新的时候是并行更新的。 但是Gauss-Seidel则是一个个元素更新的。

更新$$x^{(k+1)}*{1}$$的时候会用到$$x^{(k+1)}*{0}$$，同理，更新$$x^{(k+1)}*{2}$$的时候会用到$$x^{(k+1)}*{0}$$和$$x^{(k+1)}\_{1}$$。

（然而并没什么卵用，不比Jacobi快多少，还不能并行计算了。。。）

%accordion% Numpy实现 %accordion%

```python
def gauss_seidel_iteration(A,b,init_x,iters=10):
    dim = init_x.shape[0]
    x = init_x
    result = np.zeros((iters+1,dim))
    result[0,:] = x.reshape(-1)

    diag_A = np.diag(A)
    D_inv = np.diag(1.0/diag_A)
    LU = np.diag(diag_A) - A
    B = D_inv @ LU
    D_inv_b = D_inv @ b

    for i in range(iters):
        for d in range(dim):
            x[d] = B[d,:] @ x + D_inv_b[d]
        result[i+1,:] = x.reshape(-1)
    return result
```

%/accordion%

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b2664bcfb3c7708f7cf8443edc1a11be6081037f%2F2020-07-25-12-56-57.png?alt=media)

### 收敛性分析

如果矩阵A每一行的“除了对角线元素之外，其它元素的和”都要小于对角线元素的话，则这个矩阵一定可以被Jacobi迭代求解（我也不知为何如此，不过应该可以从这个条件推出谱半径小于1）。

还有一个就是B矩阵 （$$B=D^{-1}(L+U)$$） 的谱半径，谱半径等于最大的特征值。每个向量都可以被分解为特征向量的和（也就是表达在某个以特征向量为基的空间），如果谱半径大于1，那么求解的时候就会发散。谱半径不仅要小于1，而且要越小越好，这样收敛才够迅速。

而为什么我们关心B的谱半径呢？

我们站在神的视角，已经知道了真实的解x，也就是说x绝对满足： $$x=Bx + D^{-1}b$$，那么不妨设置误差 $$e^{(k)} = x^{(k)} - x$$

每一次迭代的时候，我们看作：$$e^{(k+1)} + x = B(e^{(k)}+x) + D^{-1}b$$

所以有：$$e^{(k+1)} + x = Bx + D^{-1}b + Be^{(k)}$$

我们知道 $$x = Bx + D^{-1}b$$ 所以消去等号两侧的两项，得到：

$$e^{(k+1)} = Be^{(k)}$$

所以，很直观的看到，只有谱半径小于1，才能保证误差不断减少，即所谓收敛。

## 梯度下降

梯度下降迭代法的思路是优化 $$x = argmin\_z | Az - b |$$

所以Loss Function就是 $$L = x^TAx - b^Tx$$

$$
\frac{\partial{L}}{\partial{x}}=Ax-b
$$

梯度方向有了，接下来就是学习率了，和机器学习任务不一样，这里的学习率是可以有最优解的， 原理大概是：如果沿着梯度方向前后走，并且记录路径上L的大小，那么我们会得到一个抛物线。

抛物线，可是有极小值的啊！！！

$$
\frac{\partial}{\partial{\alpha}} (f(\alpha)^TAf(\alpha)-b^Tf(\alpha)) = 0
$$

其中 $$f(\alpha) = x - \alpha (Ax-b)$$

或者说，走到新的点以后，其梯度应该和当前的梯度正交，这两种表述是等价的：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-47ff3aa0b6ce41ff7a9354cae495e6c48aae7c0f%2F2020-07-24-23-00-21.png?alt=media)

最优解是：

$$
\alpha = \frac{x^Tx}{x^TAx}
$$

过程太难打暂略。

根据这个性质，梯度下降的算法将会以互相垂直的折线路径快速逼近最优解。

%accordion% Numpy实现 %accordion%

```python
def gradient_desc(A,b,init_x,iters=10):
    dim = init_x.shape[0]
    x = init_x
    result = np.zeros((iters+1,dim))
    result[0,:] = x.reshape(-1)
    for i in range(iters):
        ax = A @ x
        grad = ax - b
        flat_x = x.reshape(-1)
        lr = np.dot(flat_x,flat_x)/np.dot(ax.reshape(-1),flat_x)
        x -= grad * lr
        result[i+1,:] = x.reshape(-1)
    return result
```

%/accordion%

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8ee8db9a0658b541c098aa43da44c3371225d8bd%2F2020-07-25-12-57-21.png?alt=media)

## 共轭梯度法

这个方法的思路是，不走回头路，如果每一次新的搜索方向都与之前的所有搜索方向张成的子空间共轭（即A-正交），那么可以更加高效。

但是求出一个正交的子空间（比如用施密特正交）复杂度他娘的是 $$O(n^3)$$ 啊。

## Warm Start

在解微分方程（组）的时候，由于上一个时间片的状态和这个时间片的状态相差并不多，所以可以用上一个时间片的状态作为这个时间片的初始值输入，这样可以大大增加求解的效率。

## MultiGrid

## References

### 测试代码

%accordion% 测试框架 %accordion%

```python
import numpy as np
from matplotlib import pyplot as plt

def solver_visualizer(A:np.ndarray,b:np.ndarray,x:np.ndarray,steps:int=100):
    """
    A: 2x2 matrix
    b: 2x1 vector
    x: nx2 vectors
    """
    ax_min = x[:,0].min()
    ax_max = x[:,0].max()
    rx = ax_max - ax_min
    ay_min = x[:,1].min()
    ay_max = x[:,1].max()
    ry = ay_max - ay_min
    X,Y = np.meshgrid(
        np.linspace(ax_min - 0.2 * rx,ax_max + 0.2 * rx,steps),
        np.linspace(ay_min - 0.2 * ry,ay_max + 0.2 * ry,steps),
    )
    E = np.linalg.norm(
        A @ np.array([X.reshape(-1),Y.reshape(-1)]) - b, 
        axis=0
    ).reshape(
        X.shape
    )
    plt.figure(figsize=(10,6))
    plt.contourf(X,Y,E)
    plt.contour(X,Y,E)
    plt.plot(x[:,0],x[:,1],'-o')
    plt.show()

A = np.array([[1,0.2],[-0.3,2]])
b = np.array([0.6,0.8]).reshape(2,1)
init_x =  b / np.diag(A).reshape(2,1)

# Example

solver_visualizer(A,b,gauss_seidel_iteration(A,b,init_x,10))
```

%/accordion%

### 参考文献

* [An Introduction to the Conjugate Gradient Method Without the Agonizing Pain](https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf)
  * [共轭梯度法通俗讲义](https://flat2010.github.io/2018/10/26/%E5%85%B1%E8%BD%AD%E6%A2%AF%E5%BA%A6%E6%B3%95%E9%80%9A%E4%BF%97%E8%AE%B2%E4%B9%89/)

### 软件库收集

* [MKL Sparse Solver Routines](https://software.intel.com/content/www/us/en/develop/documentation/mkl-developer-reference-fortran/top/sparse-solver-routines.html)
* [PARDISO](https://www.pardiso-project.org/)
* [Sparse Matrix Solvers on the GPU: Conjugate Gradients and Multigrid](http://www.cs.jhu.edu/~misha/ReadingSeminar/Papers/Bolz03.pdf)
* [cuSOLVER](https://docs.nvidia.com/cuda/cusolver/index.html)


# 弹簧质点系统

## 受力分析

弹簧质点系统遵循胡克定律和牛顿运动定律：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7ea503f7d4bc8b27fb683f8cdc5771ea67eb0f00%2F2020-07-19-20-03-31.png?alt=media)

并不复杂（当然需要考虑重力）。

## 时间积分

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3aa50fd5868470508cfb2043a0de89eb4aaea3b9%2F2020-07-19-20-05-20.png?alt=media)

### 前向欧拉

通过当前的状态推测下一步的状态

* 通过当前的受力分析计算加速度，推断下一步的速度（ $$\times \delta t$$ ）
* 通过当前的速度计算下一步的位移

### 半隐式欧拉 （AKA. Symplectic Euler 对偶欧拉）

* 通过当前的受力分析计算加速度，推断下一步的速度（ $$\times \delta t$$ ）（和上面一样）
* 用计算出来的新速度计算下一步的位移

实现的时候，在更新计算和地面的碰撞的时候，更新位置之前首先计算和地面的碰撞（`pos.y<bottom_height?`），如果碰撞了，把速度设置为0。

上面两种都是显式时间积分器，问题在于对步长有限制，不可太大，否则容易爆炸：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f72ce2f6f7714abad10edf1a6e5ed94dd9d0d024%2F2020-07-19-20-58-11.png?alt=media)

这个公式的原因是：超过了Nyquist采样频率（TODO 找一些文献）

### 后向欧拉

隐式时间积分不止这一种，还有Middle-Point之类。

缺点：

* 难以实现，难以优化
* 每一步都会变得更加昂贵

优点：

* 可以容忍更加大的步长

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-88d55b1fa64456c9d0644459838f346b531abdcb%2F2020-07-19-21-05-51.png?alt=media)

可以看到，1和2互相依赖，所以代入求解：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7dd418faea3319652a40e0cec74c3229b6da1f4d%2F2020-07-19-21-08-13.png?alt=media)

按照道理说，求逆矩阵是最好的，但是时间复杂度不可接受，所以我们用雅可比迭代之类的方法替代：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a866e3ccc45ece42ac05e1bd774d77cbb1e0a364%2F2020-07-19-21-10-42.png?alt=media)

雅可比迭代的一个实现：

```python
@ti.kernel
def iterate():
    for i in range(n):
        r = b[i]
        for j in range(n):
            if i != j:
                r -= A[i, j] * x[j]

        new_x[i] = r / A[i, i]

    for i in range(n):
        x[i] = new_x[i]
```

但是雅可比迭代使用有限制，只能收敛一些性质比较好的矩阵，对谱半径有一些要求。

使用共轭梯度的话就会更快一点。

从这里就可以看出来不同求解器的区别和联系：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8adcf66672aa573a8015f65eb1359f56b0e3c76d%2F2020-07-19-21-15-14.png?alt=media)

### 大规模求解器

（仅仅是索引）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-856d9621cef5dae4164253e92726bb37773d1eb7%2F2020-07-19-21-17-44.png?alt=media)

### 实践记录


# 光滑粒子法

## 简介

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b8b17344739d2bbc6d9931633b86620bae3bb9cd%2F2020-07-23-13-12-12.png?alt=media)

* 不需要Mesh，适合描述自由表面流体（水，烟不是自由表面流体）

## WCSPH

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bc3081ebaebb6bb924808d17b56b20f92ae52e50%2F2020-07-23-13-15-02.png?alt=media)

用于描述不可压缩的流体。

这里有个神奇的公式：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-50584c16b0224035c25823bfd78dd638383a8f9e%2F2020-07-23-13-22-59.png?alt=media)

严格来说这个公式是不对的，但是这个公式能保证动量守恒，否则有各种奇怪问题。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-532b1d56c2bf8ffa6450dbcf585370f26f68e2df%2F2020-07-23-13-24-08.png?alt=media)

所以SPH的写法就是：

第一个公式（左）是欧拉方程，或者说是无粘流体的纳维-斯托克斯方程。右边是压强的计算公式。

1. 计算每个粒子的密度，主要取决于周边粒子的数量
2. 用那个恶心的一批的公式计算压强的梯度
3. 和弹簧质点模型一样，计算新的v和位置

## 一些变种

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f16b3ab22199d7a9886221c99b0476e3b2b7dddb%2F2020-07-23-13-29-40.png?alt=media)

第一个PCI-SPH，类似隐式时间积分，能得到更好的速度场（接近无散场），也就是更加的“不可压缩”。


# 机器学习相关博客


# 一般理论

这里记录机器学习中较为通用的理论，例如正则化，梯度下降，各种指标和Loss等等。

但是因为这部分内容过于硬核所以我也没有写好的自信。


# 收藏夹

## 博客

* [编写高效的PyTorch代码技巧](https://zhuanlan.zhihu.com/p/137965337)
* [动手推导Self-attention-译文](https://zhuanlan.zhihu.com/p/137578323)
  * [动手推导Self-attention](https://zhuanlan.zhihu.com/p/130509584)
* [One Shot Multi-Object Tracking Overview](https://zhuanlan.zhihu.com/p/143798072)

  **开源项目**
* [dmmiller612/sparktorch](https://github.com/dmmiller612/sparktorch):Spark训练Pytorch模型的方法


# 论文阅读：“快把卷积神经网络中的平移不变性带回来”

## 简介

这篇博客主要是我读论文[Making Convolutional Networks Shift-Invariant Again](https://arxiv.org/pdf/1904.11486.pdf)的读书笔记，夹杂了自己直白和幼稚的理解。因为我相信所有的创意无论最后的结果多么复杂，最初的想法一定都是直观的。

## 卷积

其实说清楚这件事情，我们需要从卷积开始。 其实卷积（或者说滤波吧，两者差个卷积核的Flip，这里姑且混为一谈）操作本身是一个平移两个信号并且积分的操作，从定义本身就是可以做到平移不变的。 真正让卷积做不到平移不变的，是MaxPool。

计算资源的制约让我们一定要做Pool操作（准确地说是降采样），但是降采样的时候一定会丢失信息，例如步长为2的降采样就是丢掉一半的信息？

如果仅仅是丢掉一半的信息似乎还没有这么严重，因为图片中一些纹理的信息本身就是冗余的，丢了一半，还有一半，问题不大。

但是事实上是，我只是给图片稍稍平移了一下，我的模型就尼玛疯了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c104c941f202dddeaf47e2b1463de1d55ef00e4c%2F2020-07-07-18-03-05.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-48b2d4c03e856f3947e0878a1fa9fa1d2e9aaa9b%2F2020-07-07-18-03-52.png?alt=media)

## 邪恶的MaxPool

其实AvgPool都没有这个问题，为啥偏偏MaxPool有？既然有问题，为啥我们还偏偏要用MaxPool？

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c619de0882fb7c990f40212dde215c287e7050ce%2F2020-07-07-12-28-05.png?alt=media)

可以看到，仅仅是平移了一个像素，MaxPool的结果就有可能完全不同。

既然MaxPool这么不好，我们为什么还要用呢？因为我们需要。举个简单的例子：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bab008cc1faed4c1d10ef62694b99841ebf38a91%2F2020-07-07-17-38-53.png?alt=media)

我们需要的是这张图片分类，那么应该返回的是1（有猫），而不是猫占图片的面积比例。

一个频域上的解释是：MaxPool倾向于保留中高频信息，而网络的卷积核中，大多数是提取中高频信息的， 我尝试提取了VGG和MobileNet的部分卷积核，并且绘制了傅立叶变换以后的结果（仅保留幅度）。 ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5d68003813b4cd7cd4182be639fabb005d478436%2F2020-07-07-17-42-27.png?alt=media) ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e9f591bfd023895bc3b6c519a27c73808fb36d71%2F2020-07-07-17-43-38.png?alt=media)

## 解决方案

那我们怎么解决这个问题呢？

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f01b1d09ee02b35172113e8c0105b7ee4ed87207%2F2020-07-07-17-44-08.png?alt=media)

作者提出了，MaxPool可以看成Max+Pool（subsample）的组合，我在中间插入一个Blur Kernel（我感觉长得有点像高斯核）可以有效的抑制这个情况的发生。

从测试结果可以看出，加了Blur核的MaxPool情绪稳定多了：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-1010eaa2205627333b8679d4f430af9b8286912a%2F2020-07-07-17-45-57.png?alt=media)

而且最神奇的是，按照道理说，这样的处理可以增强平移不变性，但是会损失精度。 但是测试的结果居然是：精度也提升了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ec41acdefc8cd11e172bedd0b40ce1129e1ee7b2%2F2020-07-07-17-46-55.png?alt=media)

## Hack

作者测试了MaxPool和MaxBlurPool在不同的层的情况，可以看到，随着层数变深，平移引起的变化越发严重，而使用了MaxBlurPool以后，虽然不能完全消除影响，但是能减轻很多了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-24bd8a861789c1db855e90c4a7956f751918ae39%2F2020-07-07-17-47-20.png?alt=media)

作者的改动是，MaxPool的中间加入了一个Blur Kernel，而卷积的另外一个解释，则是加权平均。我们可以理解为，Max之后，在做了一个加权平均（Weighted-Avg）之后再做Pool。 我个人对此的理解是：我们需要高频数据，但是不需要那么高频的数据也可以。所以可以作出一个介于AvgPool和MaxPool之间的Pool来，在这个上面作出的模型，效果要好于单纯的使用MaxPool。

## 参考

* [Making Convolutional Networks Shift-Invariant Again](https://arxiv.org/pdf/1904.11486.pdf)
* [Why do deep convolutional networks generalize so poorly to small image transformations?](https://arxiv.org/pdf/1805.12177.pdf)
* [Shiftable multiscale transforms](http://persci.mit.edu/pub_pdfs/simoncelli_shift.pdf)


# 手部检测和手势识别


# 再谈正则化项

## 前言

今天是除夕，马上过年了，先给大家拜个早年，感谢大家还没有取关我这个拖更了半年的鸽王。 我们今天要讨论的是（广义的）正则化项，从另一种角度来讨论。 说到正则化，每一个做机器学习的同志都不会陌生。做机器学习的过程需要花上很大的力气和过拟合作斗争。 但是原则上来说，我是不喜欢（狭义的）正则化项，因为不直观，凭什么参数的平方和这种东西会和模型复杂度有关系呢？为什么是平方和不是四次方和呢？或者是绝对值和？ PRML上有一张很有名的图片用可视化的方法说明了正则项的实际意义，但是仍然让我们觉得这样不OK，凭什么这一种正则化方法可以用在这么多的任务上并且保持有效？

## 那咋办？

我的结论就是不要用这种正则化项。或者说，少用。除非你希望参数稀疏的时候来上一个L1的正则化之类的。

如果没有正则化项，我们有什么办法控制模型的复杂度呢？

例如推荐系统，在做嵌入（Embedding）的时候，主动约束Embedding的维度，仅仅用8维到16维就可以表达一个用户，层数也减少，这样的训练就更加漫长了，但是小心翼翼的维持着“卧槽模型复杂度不够导致精度不行”和“卧槽模型复杂度太高训练几步就过拟合了”之间的平衡的话，能拿到一个相当不错的成绩。

## 还有什么别的好办法吗？

其实大家熟知的CNN，就是基于平移不变性假设的权值共享。在CNN之前，我们处理手写数字这样的数据集，是把28\*28的图片直接Flatten的，显然这样的方法或许能工作但是极容易过拟合，如果想要不过拟合，那就要准备好海量的数据。

所以解决方案也就呼之欲出了：**强行让模型符合一定的假设。**

这就是今天，我读了一篇论文：[Representation Learning-Assisted Click-Through Rate Prediction​](https://arxiv.org/abs/1906.04365) 之后的感受。简单的看上去这篇文章和今天的问题没什么关系。容我细细道来。

首先文章定义了一个模型：

![model](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4cda267b747afd18b7e7540b986f90126a476212%2F2020-05-05-22-39-46.png?alt=media)

这个模型有三个部分，预测，匹配，关联。本质上是可以独立训练的三个网络。 第一个是CTR预估模型，它的长相平平无奇（详情见论文）。 第二个是相似度匹配模型，它讲用户和广告各进行一个变换以后，进行一次内积，通过内积的数值求出相似度，它的相似，本质上就是点击。 第三个网络其实是个伪装的很好的Word2Vec模型（雾）。用于计算广告和广告之间的PY关系。 我讲的比较简单，这位仁兄说的稍微详细些：

[Jesse：（读论文）推荐系统之CTR预估-DMCP模型](https://zhuanlan.zhihu.com/p/102075293)

训练的时候要三个网络一起训练，但是预测的时候只用CTR，这一开始让我无论如何也想不通，如果你的目标是CTR，为什么不让LOSS直接针对CTR进行训练呢？ 其实这另外的两个网络根本就不是要训练的部分，它们只不过是第一个网络的正则化项而已。这里面的一个大前提就是，我底下的这些Embedding，它们应该是能表达用户和广告的，并不仅仅是做预测（这也是Embedding层设计的初衷），这样的设计在早期的NN方法中，会用ALS算法的结果做Embedding层的初始化就可见一斑。但是End2End的方法流行以后，网络并不会在乎人类最初是怎么设计这一层的，网络只关心Loss。 回到这篇文章，我们可以看出，作者本质上假设了，这些Embedding，除了预测CTR以外，理论上也能承担其他任务，例如关联和匹配等等，这些才是Multi Task的本质，其它的Task都是为了某一个具体的Task服务的正则化项，并不是我真的需要这些Task。

如果我们继续回顾，还会有更多细思恐极的细节：

\[Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks]​(<https://arxiv.org/abs/1604.02878>)

这是一篇很早（也还行）的人脸检测的论文，当然，也算是SOTA模型之一了，效果很棒，就是全卷积部分上GPU Batch困难（扯远了）。 其中的P/R-Net，只需要做 Face Classification，就可以了，但是训练的时候仍然加入了boundbox和landmark的相关Loss，本质上，剩下的这俩Loss也是一种正则化。

## 总结

如果我们检视多任务训练的框架，我们就会发现，很少有“我们真的要用这个网络完成好几件事情”的情况，更多的，往往是成为主要目标的约束，防止网络放飞自我而过拟合。


# 半监督/无监督学习

## 无监督

* [Unsupervised Moving Object Detection](https://arxiv.org/pdf/1901.03360.pdf)
  * [antonilo/unsupervised\_detection](https://github.com/antonilo/unsupervised_detection)
* [Self-training with Noisy Student improves ImageNet classification](https://arxiv.org/abs/1911.04252)
  * [Self-training with Noisy Student](https://zhuanlan.zhihu.com/p/164597142)
  * [Self-training在目标检测任务上的实践](https://www.jiqizhixin.com/articles/2020-08-03-6)
* [Unsupervised learning of foreground object detection](https://arxiv.org/pdf/1808.04593.pdf)
* [何恺明一作，刷新7项检测分割任务，无监督预训练完胜有监督](https://www.jiqizhixin.com/articles/2019-11-15-6)
* [2020年国内最新最优目标检测（完结篇）](https://cloud.tencent.com/developer/article/1613547)
* [目标检测算法进展大盘点](https://zhuanlan.zhihu.com/p/112942266)
* [AAAI 2020 论文解读：商汤科技提出新弱监督目标检测框架](https://www.infoq.cn/article/yzldalqwipqbijo5xlah)

## 半监督/弱监督

* [ICLR2021 Oral ｜利用一个样本估计类别数据分布 9行代码提高少样本学习泛化能力](https://zhuanlan.zhihu.com/p/344531704)
  * 只适合分类问题


# 推荐系统相关博客


# 关于AA测试和AB测试的一些思考

## 起因

一般的AA/AB测试都怎么做呢？正常来说，取一部分用户，然后把用户分成2组（也可以是多组，本文中我们只讨论最典型的2组的情况），在一段时间的运行以后，比较其结果。

AB测试自不必说，主要测试新的算法在指定的监测指标上又没有达到预期，AA测试则一般用来测试AB测试和用户的稳定性，也可以作为AB测试的一个预演和对AB测试工具的测试。 有了AA测试，可以对AB测试的结果更为自信，当然，也有很多的文章讨论AA测试究竟是不是必要的，例如这一篇：[A/A Testing: A Waste of Time or Useful Best Practice?](https://www.abtasty.com/blog/aa-test-waste-of-time-or-useful-best-practice/)。

就我的实践而言，AB测试中的确容易出现两组天然存在指标差异的情况，尤其是样本或者用户少的时候更容易发生，比如有的时候你就是不幸多分了几个Heavy User到某一个组去，导致你怎么测试都得不到正确的结果……当然，在实践中，这个问题造成的误差并不大，一般CTR误差都在0.1%这个量级，而且误差可以随着时间收敛，基本算法带来的提升都应该能覆盖这个误差。

AB测试中出现两组天然存在指标差异带来的更大问题是，AA测试通不过，有的时候换个分组的Salt Key结果就不一样了。造成这个问题的原因是，随着采样数据的上升，AA测试中两组的方差都逐渐收敛，这个时候两组之间的天然差异会被“固化”，这个时候AA测试就失败了。

这个问题困扰我们挺久，考虑到我们之前的AA测试只有Pass/Fail两个结果，所以不妨**把AA测试变成标定AB测试的工具**，故事就从这里开始了。

## 改善

假设在一次试验（或者试验的某个片段中）收集到如下数据：

| Group       | Variable | Comment  |
| ----------- | -------- | -------- |
| T(reatment) | $$N\_1$$ | 测试组的展示次数 |
| T(reatment) | $$C\_1$$ | 测试组的点击次数 |
| C(ontrol)   | $$N\_2$$ | 对照组的展示次数 |
| C(ontrol)   | $$C\_2$$ | 对照组的点击次数 |

我们假定点击这个事件是服从Bernoulli分布的，也就是每次展示等价于抛一枚正反面概率固定为p的硬币（这里的假设是错误的，爆款新闻，突发事件，甚至只是单纯的周末或者深夜等等特殊时间都会影响点击率）。 那我们做了N次这样的试验，所以 $$C \sim Bin(N,p)$$，C指的就是点击次数这个随机变量，p是二项分布试验的概率，那么C/N自然就是实际观测到的点击（转换）率CTR了，之后我们用符号x表示这个变量。

根据中心极限定理，当**n特别大**的时候，C是服从正态分布的（注意了，N一定要特别大），表达为 $$C \sim N(Np, Np(1-p))$$。 我们稍作转换$$x \sim N(p, \frac{p(1-p)}{N})$$。

我们用表中的数据代入，我们就可以得到对照组和测试组的点击率分布。 记作：$$x\_1 \sim N(\mu\_1,\sigma\_1^2)$$ 和 $$x\_2 \sim N(\mu\_2,\sigma\_2^2)$$。 其中，$$\mu=\frac{C}{N}$$，$$\sigma^2=\frac{\mu(1-\mu)}{N}=\frac{N(N-C)}{N^3}$$。

这里我们遇到第二个有点脏的点：用观测的CTR取代$$\mu$$，好在CTR是$$\mu$$的无偏估计，N又特别大，忍了。

直观的画出来，大概长这个样子：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-67da8b9525f3c44826f4a22ecbfe847c1060fbe5%2F2020-09-19-13-00-10.png?alt=media)

我觉得两个随机变量还是太多，由于我们关注的并不是CTR本身，而是CTR的差异。 不妨设 $$x\_3=x\_1-x\_2$$，我们可以根据[正态分布求和公式](https://en.wikipedia.org/wiki/Sum_of_normally_distributed_random_variables)轻易得到 $$x\_3 \sim N(\mu\_1-\mu\_2,\sigma\_1^2+\sigma\_2^2)$$。

到此为止，我们所有的理论准备完成。

## 实战

### AA测试

AA测试，主要是估计一个提升的下确界，也就是我们最少要提升多少CTR，这个CTR甚至有可能是负数。举个例子，当试验组就是比对照组CTR要小一些的时候，哪怕在之后的测试中两者持平，我也可以判定对照组胜利，这和于老爷子出门没捡着钱就算丢的理论有异曲同工之妙。

在求这个数值之前，我们首先要确定一个单侧的置信度，比如0.05，或者0.01，根据你的容忍程度决定。随后我们可以根据这个数值，计算我们最少应该提升的CTR，如果形象的画个示意图，差不多是这个样子：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-94569afe6bb5b6295a34880908e5b864a501b18f%2F2020-09-19-13-59-43.png?alt=media)

简单的写个代码实现一下：

```python
from scipy.stats import norm
from math import sqrt

def get_minimal_lift(C_1, N_1, C_2, N_2, confidence_level):
    mean_1 = C_1 / N_1
    mean_2 = C_2 / N_2
    delta_mean = mean_1 - mean_2
    var_1 = mean_1 * (1 - mean_1) / N_1
    var_2 = mean_2 * (1 - mean_2) / N_2
    return norm(delta_mean, sqrt(var_1 + var_2)).ppf(1 - confidence_level)
```

### AB测试

我们用AA测试得到了一个最少提升的参数，不妨记这个最小提升为$$\epsilon$$。

那我们AB测试究竟过了没有（过了的意思是，测试组的算法对于对照组来说有提升），或者说，我们能以怎样的置信度判断AB测试过了。 也就是说我们要求：$$P(x\_1-x\_2 \ge \epsilon)$$。由于$$x\_3=x\_1-x\_2$$，所以也就是判断 $$P(x\_3 \ge \epsilon)$$。

根据公式很容易得到：

$$P(x\_1-x\_2 \ge \epsilon)=P(x\_3 \ge \epsilon)=1-\Phi\_{\mu\_1-\mu\_2,\sigma\_1^2+\sigma\_2^2}(\epsilon)$$

简单的写个代码实现一下：

```python
from scipy.stats import norm
from math import sqrt

def get_passed_prob(C_1, N_1, C_2, N_2, epsilon):
    mean_1 = C_1 / N_1
    mean_2 = C_2 / N_2
    delta_mean = mean_1 - mean_2
    var_1 = mean_1 * (1 - mean_1) / N_1
    var_2 = mean_2 * (1 - mean_2) / N_2
    return 1.0 - norm(delta_mean, sqrt(var_1 + var_2)).cdf(epsilon)
```

## 展望

其实我们不仅仅可以使用正态分布，我们还可以借鉴Thompson Sampling中的思路，利用Beta分布对CTR进行建模，而Bandit本身，其实可以看作一个实时的，特殊的AB Testing。详细可以参见这篇：[Re：从零开始的Multi-armed Bandit](https://github.com/TsingJyujing/blogs/tree/8964965982ae3f23cd47250219a5a7981b6fdd8a/ml/rl/mab-summary.html)。

但是我还没搞出来，因为还没有得到CTR误差的解析解。而Thompson Sampling是利用一种类似蒙特卡洛的方式来解决这个问题的。

等我下次无聊的时候，或许会尝试去计算一下吧。

### 更新：关于如何利用Beta分布进行AB测试（Bayesian Approach）

之前我提到，我没有求得Beta分布的解析解。

> 而Thompson Sampling是利用一种类似蒙特卡洛的方式来解决这个问题的。

我简直是个蠢货，其实解决方案已经被我写出来了而我不自知。我的同事今天给出了一个不算漂亮但是又极其有效的解决方案：采样。

假设我们现在通过AA/AB测试获得了数据，求出了C组的分布 $$x\_c \sim Beta(a\_c, b\_c)$$ 和T组的分布 $$x\_t \sim Beta(a\_t, b\_t)$$ ，我们现在要求期望 $$E(x\_t - x\_c)$$ 或者方差标准差之类的统计参数，直接用[scipy.stats.beta](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.beta.html)的 `rvs` 生成一些样本，将它们相减以后获取新的分布。

仔细反省一下为什么我会这么蠢，因为在我看来，AA/AB已经是一种采样了，我们很难想象用预估出来的参数再做采样，这会导致一定程度的失真。

但是换个角度想，如果只是为了求得一个封闭解不存在或者很难求的函数的数值解，蒙特卡洛也不是不能接受，采样频率调高一点就能获取足够的精度。一直自觉是个工程师，但是脑子居然在这种地方卡壳了实在是不应该。

参考资料：

* [scipy.stats.beta](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.beta.html)
* [Understanding empirical Bayes estimation (using baseball statistics)](http://varianceexplained.org/r/empirical_bayes_baseball/)
* [bayesian ab testing](https://docs.pymc.io/en/v3/pymc-examples/examples/case_studies/bayesian_ab_testing.html)


# 随想： BPR Loss 与 Hinge Loss

在之前的文章里，我们在[加速Ranknet](/ml/recsys/ranknet)的时候意外的得到了BPR Loss：

$$
L\_{\omega} = - \sum\_{i,j \in S}{log(sigmoid(s\_i-s\_j))}
$$

我们说过，Ranknet不一定用NN，任何可微的模型都可以塞到后端去。其实我们一直没有用贝叶斯的观点来看Ranknet，只是给分数的差值加了一层 sigmoid 之后使用 二元交叉熵损失函数，四舍五入一个亿…啊不对…等于是Logistic回归了，结果就导出了BPR Loss一样的形式。

![这不巧了吗，这不巧了吗](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6587dc1df578913da9fcee197bf6940c9475b704%2F2020-08-03-23-39-24.png?alt=media)

嗯？Logistic回归？也就是说只要是分类模型都可以往上咔咔猛造咯？那为啥一定得是Logistic回归呢？我们可以把身子换了，那干脆把头也换了？

其实，平时还会用到另一个Loss，Hinge Loss（合页损失），一般用在SVM上：

$$
L\_{\omega} = \sum\_{i,j \in S}{max(0,1-s\_i+s\_j)}
$$

那SVM效果会比Logistic回归好多少吗？不会的，把Loss曲线画出来一看就知道了：

```
x = [-5:0.01:5];
plot(x,max(0,1-x),x,log(1+exp(-x)))
legend('hinge','BPR')
```

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-46e8b34d19ef269efd9c5d112e846c540eb29a6a%2F2020-08-03-23-32-49.png?alt=media)

简直就是：

![异父异母的亲兄弟啊](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8602c47ca7c2418cc4d4381ffd0cb6f661cd378b%2F2020-08-03-23-35-46.png?alt=media)

我在我们的系统上做了一些离线实验，结果是BPR略微胜利，我认为原因可能在BPR更加嫩滑…啊不对…光滑上。但是就计算量而言，肯定是Hinge Loss要远远小于BPR Loss。


# 资料收集

## 博客

* [见微知著，你真的搞懂Google的Wide\&Deep模型了吗？](https://zhuanlan.zhihu.com/p/142958834)
* [知识蒸馏在推荐系统的应用](https://zhuanlan.zhihu.com/p/143155437)
* [Ctr 预估之 Calibration](https://zhuanlan.zhihu.com/p/142086309)
* [推荐系统中稀疏特征Embedding的优化表示方法](https://zhuanlan.zhihu.com/p/141517705)
* [Learning to Rank学习笔记--ListwiseRank](https://zhuanlan.zhihu.com/p/66514492)
* [推荐系统 embedding 技术实践总结](https://zhuanlan.zhihu.com/p/143763320)

## 开源项目

* [wzhe06/CTRmodel](https://github.com/wzhe06/CTRmodel):Spark上的CTR模型
  * Naive Bayes
  * Logistic Regression
  * Factorization Machine
  * Random Forest
  * Gradient Boosted Decision Tree
  * GBDT + LR
  * Neural Network
  * Inner Product Neural Network (IPNN)
  * Outer Product Neural Network (OPNN)
* [jrzaurin/pytorch-widedeep](https://github.com/jrzaurin/pytorch-widedeep):Pytorch实现的Deep & Wide
* [allegro/allRank](https://github.com/allegro/allRank): Pytorch 实现的 Learning To Rank
* [haowei01/pytorch-examples](https://github.com/haowei01/pytorch-examples): Pytorch的，L2R，CF算法的例子。
* [microsoft/recommenders](https://github.com/microsoft/recommenders): 微软的推荐系统最佳实践


# 向量稳定化

最近接触到的一篇论文：[Orthogonal Low Rank Embedding Stabilization](https://dl.acm.org/doi/10.1145/3705328.3748141)

## 为什么我们要向量稳定化？

在训练机器学习模型的时候，尤其是推荐系统的模型的时候，有很多的模型会输出User或者Item的Embedding。这些Embedding一般来说很难在下游的任务中使用，其原因是不够稳定。 在训练稍微复杂一点的模型——哪怕只是一个几层的神经网络——我们也不能保证每一次输出的向量都是一样的，因为在Loss函数中我们约束的往往是User和Item求得的Score（内积或者Cosine Similarity之类），然后基于这个内积我能对物品进行正确的排序以保证用户感兴趣的物品在前面。

这篇论文可以通过做简单的线性变换，使这一次训练输出的Embedding和上一次尽可能的接近，并且在这个过程中：

* 不需要修改现在的训练模型，只操作输出的Embedding
* 变换前后User和Item的内积不变

可以说是非常省事的方法了。

## 什么时候能用

你随便弄两个向量矩阵肯定是不行的，这里我个人的思考是：两次模型训练之后的向量空间要有相似性。

举个简单的例子，请各位读者现在想象一下现在脑海中有一个三维的点云，每一个点就代表一个User或者Item。 随后我们有另一个点云，是第一个点云经过旋转镜像之类的操作得到的。如果仅仅从Cosine Similarity或者内积的角度，这两个点云不能说是一模一样吧至少也可以说是风马牛不相及。 但是他们**内在的结构是一样的**小王喜欢键盘鼠标，那么小王的Embedding就是会和3C数码产品很接近，小李喜欢口红化妆水，那么小李就是会和化妆品的Item很近。

其实Loss和数据一样也不能保证每一次学习出来的空间也一样，但是这样的结构一致性给我们机会去变换其中一个空间。

## 具体怎么做？

我们需要做两件事：

1. （低秩的）SVD分解
2. 对齐

### 低秩SVD分解

由于我们用相同或者相似的数据训练出来的模型，所以User和Item的内积矩阵也应该是差不多的（如果你这个模型真的有效的话）。那么他们的SVD分解应该也是差不多的，所以我们做SVD分解。

但是你现在已经手握User和Item的矩阵了，我们注意到两点：

1. 传统的推荐系统模型里面，这个Score矩阵是稀疏的，但是你握着Embedding再去求Score矩阵那就稠密了。
2. 你的User和Item的Embedding的维度都是e，这个e一般不大（32，64，192之类？），SVD做出来秩不会超过你的维度e。

所以我们肯定能找到一个低秩的方法：

首先我们设定如下的变量：

* User Embedding: W 是一个 m x e 矩阵
* Item Embedding: T 是一个 n x e 矩阵

评分矩阵S就可以这样算出： $$S=TW^T$$

我们不要急，先对User和Item的Embedding做一个QR分解：

* $$T=Q\_TR\_T$$
* $$W=Q\_WR\_W$$

我们就得到了$$Q\_T$$和$$Q\_W$$两个正交的向量组，和$$R\_T$$与$$R\_W$$两个上三角矩阵，别急，等下会有用：

如果一个正常的分解$$\text{SVD}(S)=\text{SVD}(TW^T)=U \Sigma V$$

我们就可以用刚才QR分解的结果去凑SVD分解的结果：

$$\text{SVD}(S)=Q\_T R\_T (Q\_WR\_W)^T=Q\_T (R\_T R\_W^T) Q\_W^T$$

到这一步就很清晰了，我们只要对中间的小核$$R\_TR\_W^T$$做SVD就可以了

$$S=Q\_T \times SVD(R\_TR\_W^T) \times Q\_W^T=Q\_T U\_R \Sigma V\_R Q\_W^T$$

所以我们得出等价的U和V是这样的：

* $$U =Q\_TU\_R =T R\_T^{-1} U\_R$$
* $$V^T=Q\_W V\_R^T=W R\_W^{-1} V\_R^T$$

我们当然可以把T和W后面的东西拿出来作为变换矩阵$$M\_T$$和$$M\_W$$，求e×e的逆矩阵也不是很麻烦，像这样：

* $$TM\_T = U \Sigma^{-1/2}$$
* $$WM\_W= V^T\Sigma^{-1/2}$$

我们求得变换矩阵：

* $$M\_T=R\_T^{-1} U\_R \Sigma^{1/2}$$
* $$M\_W=R\_W^{-1} V\_R^T \Sigma^{-1/2}$$

事情还没有完，但是还可以更简单一点：

* 因为 $$R\_TR\_W^T = U\_R \Sigma V\_R$$
* 所以 $$R\_TR\_W^T V\_R^T = U\_R \Sigma V\_R V\_R^T$$
* 因为 $$V\_R V\_R^T = I$$ (SVD的性质)
* 所以 $$R\_TR\_W^T V\_R^T = U\_R \Sigma$$
* 所以 $$R\_TR\_W^T V\_R^T = U\_R \Sigma$$
* 所以 $$R\_W^T V\_R^T \Sigma^{-1/2} = R\_T^{-1}U\_R \Sigma^{1/2}$$
* 得到 $$M\_T=R\_W^T V\_R^T \Sigma^{-1/2}$$

我这里就写了$$M\_T$$的计算，$$M\_W$$的计算方法是一样的，就不赘述了。

连逆矩阵也不用求了，对角矩阵开个平方就搞定了。

反正最后得到

* $$M\_T=R\_W^T V\_R \Sigma^{-1/2}$$
* $$M\_W=R\_T^T U\_R \Sigma^{-1/2}$$

天才。

### 对齐（Orthogonal Procrustes）

这里比较简单，证明也写在Wiki上了：<https://en.wikipedia.org/wiki/Orthogonal\\_Procrustes\\_problem>

简单来说，就是求一个正交矩阵（变换）$$\Omega$$，使得B和变换过的A尽可能接近。

$$\underset{\Omega}{\text{minimize}}\quad |\Omega A-B|\_F$$

这个是有解析解的，只要对A和B的Gramian Matrix做SVD分解，脱去中间的特征值对角矩阵$$\Sigma$$以后把U和V乘起来就可以了。

## Spark实战

为什么用Spark举例呢？

虽说SVD或者Orthogonal Procrustes都是numpy和scipy自带的算法，但是在工业中肯定没法直接计算。Embedding一般会存储在某些分布式存储中，比如HDFS，或者某个HIVE表。把两个矩阵下载下来就很费工夫。最好的办法是直接在Spark里面分布式的把计算完成。

认识我的人可能知道我勉强算是个Flink爱好者，但是Spark仍然是大多数公司的数据处理基础，而且这样的批处理任务放在Spark里比Flink中要合适。

实际的代码设计公司内部的系统，我只挑一些核心的算法相关且业务无关的函数来讲讲。

### Low Rank SVD

我们需要两张表（Spark中的DataFrame），一张是User，一张是Item，其中一个列是Embedding(float array)。 这里我们会用到`qr_decompose`函数来求取QR分解的R矩阵，我们稍后会说明。

```python
def low_rank_svd(item_df: DataFrame, item_col: str, user_df: DataFrame, user_col: str) -> Tuple[np.ndarray, np.ndarray]:
    R_T = qr_decompose(item_df, item_col)  # R_T
    R_W = qr_decompose(user_df, user_col)  # R_W
    U_R, s, V_R = np.linalg.svd(R_T @ R_W.T, full_matrices=False)
    # Check if s has any non-positive values
    if np.any(s <= 0):  # matrix is ill-conditioned
        raise ValueError(
            "Singular values contain non-positive values, indicating an ill-conditioned matrix."
        )
    inv_sqrt_diag = np.diag(1 / np.sqrt(s))
    M_T = R_W.T @ V_R.T @ inv_sqrt_diag
    M_W = R_T.T @ U_R @ inv_sqrt_diag
    return M_T, M_W
```

其返回值是Item和User的变换矩阵。

### QR Decompose

其实Spark提供了[QR分解的函数](https://spark.apache.org/docs/3.5.5/api/python/reference/api/pyspark.mllib.linalg.distributed.RowMatrix.html#pyspark.mllib.linalg.distributed.RowMatrix.tallSkinnyQR)，但是我的环境里面运用有BUG。而且原生的函数需要创建相应的Matrix对象，我们这里的算法和Spark的矩阵计算的兼容性并不好——因为我们会需要对齐两个Embedding Matrix，而且对应的ID信息也不能丢。最后结果也要保存到HIVE中。基于以上的理由，最后我们没有选择Spark原生的矩阵计算函数。

`cross_gram_matrix`的作用是求得两个nxe矩阵的Gramian矩阵： $$G = A^T A$$

由于我们只要求取QR分解中的R，所以我们这里采取了另一种简易做法：

考虑到：$$A=QR$$

* Q: 正交向量组
* R: 上三角矩阵

我们轻易能够证明（中间的Q因为是正交矩阵，$$Q^TQ$$变成单位矩阵消掉了）

$$A^TA=(QR)^TQR=R^TQ^TQR=R^TR$$

所以我们只要把$$G = A^T A$$矩阵做一个cholesky分解就可以得到矩阵R了。

```python
def qr_decompose(df: DataFrame, col: str) -> np.ndarray:
    gram_mat = cross_gram_matrix(df, col, col)
    return np.linalg.cholesky(np.array(gram_mat)).T
```

### Cross Gram Matrix

没什么可说的，就是把1xe的行向量v求得$$v^Tv$$并且叠起来就得到了。

```python
# This is a function to calculate matrix multiplication A^T @ B in Spark
def cross_gram_matrix(df: DataFrame, colA: str, colB: str) -> np.ndarray:
    """
    df: DataFrame with columns colA and colB, each containing np.ndarray of shape (e,)
    colA: name of the column for matrix A
    colB: name of the column for matrix B

    Returns:
    A^T @ B as a numpy ndarray of shape (e, e)
    """
    e = len(df.select(colA).first()[0])

    def add_outer_product(rows):
        part_result = np.zeros((e, e))
        for row in rows:
            part_result += np.outer(row[colA], row[colB])
        yield part_result

    def reduce_matrices(mat1, mat2):
        return mat1 + mat2

    return df.rdd.mapPartitions(add_outer_product).reduce(reduce_matrices)
```

### Orthogonal Procrustes

其中，col\_to\_conv就是上文的A矩阵，一般是这一次训练的结果。而col\_base就是B矩阵，一般是上一次训练的结果。

需要注意的是：

* df里面每一行都必须是相同的User或者是Item。
  * 所以我们会做一次Inner Join取公共结果
* 一般我们只用Item做对齐，但是也可以User和Item都用。

```python
def orthogonal_procrustes(df: DataFrame, col_to_conv: str, col_base: str) -> np.ndarray:
    """
    A, B: shape [n, e]
    Returns R (shape [e, e]) s.t. A @ R ≈ B, with R orthogonal.
    """
    svd_core = cross_gram_matrix(df, col_to_conv, col_base)
    U, _, Vt = np.linalg.svd(svd_core, full_matrices=False)
    return U @ Vt
```

### 组合起来

假设我们已经得到了M\_T, M\_W，也求得了Orthogonal Procrustes的矩阵O，我们就可以得到最终的：

* User变换矩阵：`M_W @ O`
* Item变换矩阵：`M_T @ O`

然后把每一个原始向量乘上我们得到的矩阵就可以了。

### 附：我们用到的Import

```python
from typing import Optional, Tuple

import numpy as np
from pyspark.ml.linalg import DenseVector, Vectors, VectorUDT
from pyspark.sql.dataframe import DataFrame
from pyspark.sql.functions import udf
import pyspark.sql.functions as F
```

## 实战之后的一些经验

### 算法相关

这里有一些主观的东西。

根据我们的经验，SVD至少不会损失性能，但事实上，SVD变换之后的ndcg会更低一些，可能的原因是我们并不是直接使用内积或者Cosine Similarity，而是把它输入到神经网络中作为一个Feature使用。而且大约是因为我们的模型稳定性较好，每次都能习得差不多的空间。

但是出于谨慎考虑，我们没有去掉这一步。

其次，在Orthogonal Procrustes阶段，用全量的数据做对齐效果更好。其原因可能是我们对参与计算的用户做了过滤，确保他们不是不稳定的新用户或者低频用户。

### Coding相关

多写单元测试。

一旦上了Cluster，Spark的程序是出了名的难调试。所以在单元测试里面临时起一个Local的Spark Session，确保你的代码是正确的还是很重要的。 比如这里，我们就可以和Numpy或者Scipy的结果比较，确保我们得到了正确的结果。

当然，上了Spark还有其它的幺蛾子（比如依赖或者PySpark的Memory问题），但是至少单元测试能解决大部分的问题。


# Understanding LightGCN in a visualized way

In this article, I'm going to introduce LightGCN, a new algorithm for collaborative filtering problem.

## What's LightGCN

LightGCN is short for Light Graph Convolutional Neural-network, it's a new approach for collaborative filtering which published in SIGIR 2020.

So, let's talk about what's the graph, what's the convolution and why it's called "light".

### Graph

This is the graph we have:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c2e68c678479f6467f0d1a2f53e46ad1ebd6636a%2F2020-10-09-20-08-40.png?alt=media)

It can be represented as a heap of (user, item, interaction) triplets Of course, LightGCN is capable to use more than one kind of interaction (just create more LightGCN and concatenate them together).

It’s a bipartite graph of user-item interactions. The paper hasn't described but: it is capable to add user-user and item-item interactions.

### Convolution

#### How to

Let's recall about the convolution, I think convolution basically is a weighted moving average.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2181c8ab0102704bd0c3c4a0155bc969f494de5b%2F2020-10-09-20-08-41.gif?alt=media)

So, how we can extend convolutional operation to graph? The hardest problem is how to do the convolution on such a non-euclidean graph.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b4a7c1515dec817fe8328917f26f21ddf2c1a6c3%2F2020-10-09-20-14-20.png?alt=media)

Non-euclidean graph means we don’t have a certain neighborhood number. But in common CNN we have a certain number of neighborhoods.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5953a4925a71d7edaec1937bae3105040a243258%2F2020-10-09-20-15-33.png?alt=media)

Here's the solution, we just update the current node by doing a weighted sum operation from all neighbor nodes and do it again in the next layer. Then we use multi-layers to obtain high-order connectivity.

So the "kernel" of GCN basically is $$w\_{ui}$$. In my POV, it’s a low-pass filter.

Let's compare it with convolutional operation on image: it looks weird. It seems like fixing the convolutional kernel but adjusting the input signal. (Even it’s not a big deal since convolution operation is commutative.)

#### Example

So, let's take a look at this example to inspect what happened while we're doing the convolutional operator.

I made a bipartite graph manually like this:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4b9e298044f425e29036965000405c4b15888c71%2F2020-10-09-20-20-02.png?alt=media)

First of all, we have to generate a symmetric adjacency matrix.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5c5a2d00e6a86a1c17281e171c6deb82cfc29f84%2F2020-10-09-20-21-06.png?alt=media)

This is the structure of the adjacency matrix, we don't have user2user or item2item interaction here. O is the zero matrix, R is the **R**elationship matrix it’s a sparse matrix with user count rows and item count columns and the non-zero element is $$w\_{ui}$$, the weight between node u(ser) and node i(tem).

Finally, we got the adjacency matrix like this:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-65773b8a52b392f27d2e0140274903fece9c9bbb%2F2020-10-09-20-23-00.png?alt=media)

But this is not enough, the signal will be amplified after several layers, so we need to normalize it, there're several ways to do the normalization, we're using the method in the paper: $$\hat{A}=D^{-\frac{1}{2}}AD^{-\frac{1}{2}}$$ The D is the degree matrix.

This is the No.1 layer.

Actually, before No.1 layer we have a No.0 layer and it is kind special, just an eye matrix, because $$\hat{A}^0=I$$

Every node doing self-connection in the graph.

Now we're going to generate No.2 layer which adjacency matrix is $$\hat{A}^2$$

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c7ac1ab7d8a4743056922ca97f1468f081ff2af2%2F2020-10-09-20-26-27.png?alt=media)

Since after jump twice, the user will connect to some other users only and the item will connect to some other items only, so the graph will split into 2 parts.

It happened in every even layer, means layer No. 2 4 6 ...

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c85fb826df443196e915cccdc4c12630b952d43b%2F2020-10-09-20-25-39.png?alt=media)

Here's the 3rd-order of the graph, we can see most of the nodes connected.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7a3f839421c3c38f0a5740edcd25c0792179dc42%2F2020-10-09-20-27-29.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-76ca028ef71f9c55bf585081b04ce973c11eb436%2F2020-10-09-20-27-42.png?alt=media)

The rest layers can be done in the same manner.

## Aggregation

Now we have every layer's output of the signal, let's sum it up and this is the output of the GCN: Node's embedding.

$$E\_{out}=\sum\_{k=0}^{N}{\alpha\_k\hat{A}^{k}E}$$

There're several ways to sum it up, the paper chosen 1st one, means just let the weight as 1.

1. $$\alpha\_k=1$$
2. $$\alpha\_k=C^{k}\_{N}$$
3. $$\alpha\_k=\beta(1-\beta)^k$$

In deep learning, it's powerful because it's deep, so is this also doable in GCN?

The answer is NO!

Because in high-order layers we'll have the over-smoothing problem.

Like what I wrote previously, this is a kind of low-pass filter, so finally we will have a very stable result, almost every node has the same embedding.

This is No. 20 layer:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9203b83d545ee1fd32bf086ddf620ca226aa5103%2F2020-10-09-20-31-16.png?alt=media)

No. 21 layer:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f541c69e39a33812daeeffb238ac61c3828d71af%2F2020-10-09-20-31-23.png?alt=media)

And No. 22 layer:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9203b83d545ee1fd32bf086ddf620ca226aa5103%2F2020-10-09-20-31-16.png?alt=media)

See? This is over-smoothing.

## Light

Since we explained what is GCN, now let's take a look at why we called it LightGCN, what is the "Light". Because this model is transformed from another model: NGCF.

It simplified the message construction and aggregation, make it linear and direct:

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-75a377416a98d94be33f2a98a903e67d4d675562%2F2020-10-09-20-32-42.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8e9cfacd7e2b618e744d003ae139d1af8045c6e3%2F2020-10-09-20-32-59.png?alt=media)

But why we remove them?

The author did some ablation experiments on NGCF. And we found:

* the model perform better after removed feature transformation only
* the model perform worse after removed the non-linear activation function only
* The model performs much better after removing them all!

So let's remove them all! And this is the LightGCN.

## Compare With MF

For now, I already introduced all about LightGCN, but if you're careful enough, you may already found, the LightGCN finally output a node embedding and using the dot product of embeddings as the score, so basically, we can just extract the embedding result and save it as a matrix factorization model.

Not only we can use MF to inference it directly, EVEN the parameter space, and the regularization items are also totally the same. So: Why LightGCN works? What made it different?

Here's some of my personal POV, while we're doing the message bypass and aggregation, it equals to multiply a (sparse) matrix in a left-hand side part.

$$E\_{out}=\sum\_{k=0}^{N}{\alpha\_k\hat{A}^{k}E}=(\sum\_{k=0}^{N}{\alpha\_k\hat{A}^{k}})E$$

I think this matrix is kind of a constraint or said it's a special kind of regularization item, constrained the gradient bypassed to the embedding.

### More About Special Regularization

Not only in LightGCN, this technique is already applied widely.

For example, in this paper, he's trying to contribute a CTR predict model, but they add some other subtasks in training and jointed the loss.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-0411011d9f07371efe1c6f763ce085eb6c2e4c74%2F2020-10-09-20-37-16.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-af576b7302fb8546b788bea14ac6ee66ab417922%2F2020-10-09-20-37-23.png?alt=media)

Since they never used the model, why they have to train it?

In my POV, it's a special kind of regularization, which means the embedding is not free, it should complete some other tasks at the same time to avoid overfitting.

## Experiment

This is the result the author tested on some public dataset, it improved a lot.

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2c3489e690f9e4a3f1826ccd665e8fd08c39597e%2F2020-10-09-20-37-43.png?alt=media)

## Future Work

Maybe the next step we should care about:

* De-bias & fairness
* Robustness
* Exploitation & exploration trade-off

## References

* [LightGCN: Simplifying and Powering Graph ConvolutionNetwork for Recommendation](https://arxiv.org/pdf/2002.02126.pdf)
* [Neural Graph Collaborative Filtering](https://arxiv.org/pdf/1905.08108.pdf)
* [Representation Learning-Assisted Click-Through Rate Prediction](https://arxiv.org/pdf/1906.04365.pdf)


# 无采样的矩阵分解

## 前言

很久以前，有个大佬说过，集体负责制就是不负责制，真正的负责是要落实到人的。 那么反过来企业级理解，无采样其实就是全采样的意思。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-af6bd3bd514b8ee11058be506279dec813fe2ab5%2F2021-06-19-21-55-53.png?alt=media)

这里的采样，指的是负采样，所谓负，指的就是负样本，负样本是相对正样本而言的。

比如在一个购物网站，你购入了某个sku，或者在读书网站，你如痴如醉的读了某本书，那这就是平台期待你做的行为（购买、阅读、打赏、转发等），就是正。反之，点踩，拉黑，静音，视而不见等等这些就是负。

当然，用户行为有显式隐式之分，这里不作讨论。就正负样本中，正样本比较好收集，负样本（尤其是显式的）比较难收集。

难收集？难收集就不要收集咯！

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9c17a3aa0aeebb23130c82a23fa4f3e23f934426%2F2021-06-19-21-58-26.png?alt=media)

所有用户没发生过行为的，全都是负样本。然后从里面随机采样，为每个正样本拉郎配一些负样本，就可以得到训练数据集。

在这个数据集上应用Ranknet（参见这一篇：[Learning To Rank 之 RankNet](/ml/recsys/bpr_and_hinger)），就可以训练出一个像模像样的推荐系统。

可是采样也是个技术活，用户明显表示不喜欢的，采样权重是不是得高点？具体参看这一篇文章： [负样本为王：评Facebook的向量化召回算法](https://zhuanlan.zhihu.com/p/165064102)，这篇文章很好玩，我在实践中发现了和Facebook一样的结论，就是不能只用曝光未点击作为负样本，同时对负样本进行赋权也很重要。

采样这么难采，怎么办？

难采？难采就不要采咯！（梅开二度）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9c17a3aa0aeebb23130c82a23fa4f3e23f934426%2F2021-06-19-21-58-26.png?alt=media)

不采，就是全采，色即是空，这就是这篇文章的重点。我最早读到这个思路是在 *Efficient Non-Sampling Factorization Machines for OptimalContext-Aware Recommendation* 里面，后来顺藤摸瓜找到了 *Efficient Neural Matrix Factorization without Sampling for Recommendation* 。但是我意识到，这个Loss其实不止针对FM，任何可以把用户和物品变成向量，最后用内积评分的模型都可以采用这个Loss。

## 非采样Loss

进入正题，我们考虑用户对于系统有一些正样本 $$\<u, v, c>$$ ，三元组里分别是用户，物品和正样本的权重。其实这可以表达为一个稀疏矩阵，不放记作 $$R\_{uv}$$ ，横坐标为用户，纵坐标为物品，格子中就是权重，我们以往的思路就是分解这个矩阵，在分解为用户向量组和物品向量组的过程中，找到高维稀疏矩阵的低维稠密表达。

这个“找”的过程，其实就是（有损）压缩。而压缩的本质，就是找共同点，找重复处。 比如，我把`abcabcabcabc`压缩成{abc}4，就是找到了共同之处。那么用户行为矩阵 $$R\_{uv}$$ 的压缩，就是找到了用户和物品的“群组”，物以类聚，人以群分。

但是压缩，意味着新的矩阵要像老的矩阵，怎么评价像不像呢？我们用L2 Loss：

$$L= \sum\_{u \in U}{\sum\_{v \in V}{c\_{uv}(R\_{uv}-{\hat{R}}\_{uv})^2}}$$

这个 $${\hat{R}}\_{uv}$$，就是我们压缩以后的结果，但是我们先不急着展开这个表达式，我们先展开原本的式子：

$$L= \sum\_{u \in U}{\sum\_{v \in V}{c\_{uv}(R\_{uv}^2 -2{\hat{R}}*{uv}R*{uv} + {\hat{R}}\_{uv}^2)}}$$

这一步上过初中应该都能做出来，然后我们一个一个拆： ，第一项的$$R\_{uv}^2$$是常数，常数求导是0，所以直接扔掉（P.S. 如果对Loss的真实数值有执念的，这个是可以算出来的，正样本权值平方求和嘛）。 第二项 $${\hat{R}}*{uv}R*{uv}$$ 是两个矩阵Element-wise相乘，$$R\_{uv}$$只针对正样本是1，负样本都是0，所以可以简化为： $$\sum\_{u \in U}{\sum\_{v \in V^+}{c\_{uv}(-2{\hat{R}}\_{uv})}}$$ 最后一项是个硬骨头，且拆分以后会利用第二项与之合并，下面细说。

看到 $${\hat{R}}*{uv}^2$$ 会想起什么？考虑到 $${\hat{R}}*{uv}=P^TQ$$ (P,Q分别是训练中的User和Item的Embedding)，我们肯定是展开来，利用矩阵乘法的结合律求 $$\sum{P^TP \* Q^TQ}$$，但是不行，因为 $$c\_uv$$ 不是常数。

不过有困难要上，没有困难制造困难也要上，我们就干脆制造一个可以展开求和的条件。

首先我们想办法把$$\sum\_{u \in U}{\sum\_{v \in V}{c\_{uv}}}$$拆成正样本负样本两块，$$\sum\_{u \in U}\sum\_{v \in V^+}{c\_{uv}}$$简写为$$\sum\_{pos}$$， $$\sum\_{u \in U}\sum\_{v \in V^-}{c\_{-}}$$简写为$$\sum\_{neg}$$，其中，$$c\_{-}$$是给所有负样本赋的统一的值。

我们可以看到，正样本部分 $$\sum\_{pos}{c\_{uv}\hat{R}*{uv}^2}$$ 我们就可以和第二项合并到一起了，但是剩下的部分仍然不能方便的求和，因为只有负样本的$$\sum*{neg}{c\_{-}\hat{R}*{uv}^2}$$，这里，我们凭空再变一对$$\sum*{pos}{c\_{-}\hat{R}*{uv}^2} - \sum*{pos}{c\_{-}\hat{R}\_{uv}^2}$$出来，其中，前面的部分用于和剩余的负样本部分结合，第二部分加入到第二项中去。

我们梳理一下，第一项被我们轻松干掉了，第三项最后变成了$$c\_{-}\sum{\hat{R}\_{uv}^2}$$，我们用矩阵乘法运算的结合律轻松求出，第二项变成了：

$$\sum\_{pos}{2c\_{uv}\hat{R}*{uv} + (c*{uv}-c\_{-})\hat{R}\_{uv}^2}$$

看上去好复杂，其实计算复杂度一点都不高，只是正样本而已啦！

这里我给出我的Pytorch的实现，需要说明的是，这个代码我没有验证过，因为这是我从完整版简化而来，完整版里带了真负样本的计算，还有一些业务逻辑，我都给去掉了。

最后，作者的模型里是有h（最后的一层线性层）的，但是从模型看这个h完全就是冗余的，实测效果是有没有h都没差。

```python
"""
unknown_negative_weight: c_{-}
positive_weight:  c_{uv}
user_embedding: P
item_embedding: Q
core_model.h: h
positive_samples: List of [user_id, item_id]
l2_regular_factor: l2 regular factor
R_pos: \hat{R}_{uv} 正样本部分
"""

R_pos = (
        user_embedding[positive_samples[:, 0], :] *
        item_embedding[positive_samples[:, 1], :] *
        core_model.h
).sum(dim=1)

classify_loss = unknown_negative_weight * torch.sum(
    (user_embedding.t() @ user_embedding) * \
    (item_embedding.t() @ item_embedding) * \
    (core_model.h.view(1, -1) * core_model.h.view(-1, 1))
)
classify_loss += torch.sum(
    (positive_weight - unknown_negative_weight) * R_pos * R_pos - 2 * positive_weight * R_pos
)

# 总体的RMSE Loss，有物理意义的那种哦
rmse_loss_value = (classify_loss + positive_weight_sum) / weight_sum
# 正样本的RMSE Loss
positive_rmse_loss_value = torch.sqrt(((1 - R_pos).pow(2) * positive_weight).sum() / positive_weight_sum)
# 当然也可以求负样本的RMSE，这里略

regular_loss = sum(
    param.pow(2).sum()
    for _, param in core_model.named_parameters()
    if param.requires_grad
) * l2_regular_factor

loss = classify_loss + regular_loss
```

## 后记+实战结果

这个Loss很有禅意，形式很简洁，论文中实验结果表示很有效。如果抛开严谨的数学推导和一些数学技巧，我们可以粗略的看到：其实整个公式相当于先把正样本也当成负样本求和，然后再给圆回来的操作，这么理解就可以直观解释 $$(c\_{uv}-c\_{-})\hat{R}\_{uv}^2$$的意义。

但是在我实战的时候，结果显示这个Loss没有优于使用Ranknet Loss的LightGCN，我认为原因在于：我根据曝光强度，对负样本进行了分级，这个Loss做不到。

这里还需要再展开一点，我其实尝试魔改了原文的公式，把负样本加入到正样本中去，只是权重为负数，但是负样本太多了（几乎是正样本的100倍！），加上这个Loss不能分Batch训练，考虑负样本的情况下计算效率非常堪忧，丢掉部分负样本效果又下去了。 相比之下，Ranknet可以分Batch，可以在训练的时候实时采样生成样本对，计算效率和最后结果都要略微优于非采样Loss。

另外，这里不像Ranknet，正负样本是自动均衡的，怎么配平正负样本是个玄学问题，并不是正负样本权重和是1:1就好的，这个建议交给超参数调整去做（我的实验结果似乎是1：10比较好，实验做的太早了，忘了……）。

还有一点最重要的，这个Loss不支持引入Context Feature，你只能干巴巴的使用User Embedding和Item Embedding。

以上问题，我想也是实战中使用人数不多的原因。

不论如何，虽然形式简单，但是这个Loss数学上的优美仍然吸引到了我，类似的思路，今天用不到，以后遇到分解矩阵的问题或者只有正样本+无其它特征的推荐系统（比如召回模型，我这次不需要做召回所以没用上）的时候可以试一下。


# Learning To Rank 之 RankNet

先记录一下自己的梯度简化推导过程，我自己纸上推了3遍，每次纸丢了就要重新推。 特此记录，省的全尼玛忘了。

## 标准的 RankNet Loss 推导

对于Ranknet，其实是将一个排序问题（比如Top N推荐）演变成一个分类问题。 假设我们已经有一个训练好的评分器，输入User ID和Item ID能给出一个评分，那么，这个评分应该满足越相关（或者用户越喜欢）数值越大。 那么在训练这个评分器的时候，我们假定有i和j两个item，且i更加相关，那么对于分类来说满足：

$$
f(u,i)>f(u,j)
$$

换个写法：

$$
P(f(u,i)>f(u,j))=1
$$

对于一般的BCE Loss，我们有：

$$
L\_{\omega} = - \sum\_{i=1}^{N}{t\_i \times log(f\_{\omega}(x\_i)) + (1-t\_i) \times log(1-f\_{\omega}(x\_i))}
$$

其中：

* $$f\_{\omega}(x\_i)$$ 是网络的输出，范围应该在0～1之间，最后一般在Linear层后接入一个Sigmoid激活函数来达到这样的效果。
* $$t\_i$$ 是优化目标，一般来说 $$t\_i\in{0,1}$$，但是实际上允许0～1之间的任意实数。

在RankNet中

$$
L\_{\omega} = - \sum\_{i,j \in S}{t\_{ij} \times log(sigmoid(s\_i-s\_j)) + (1-t\_{ij}) \times log(1-sigmoid(s\_i-s\_j))}
$$

其中：

* $$t\_ij$$的取值为：
  * 如果$$s\_i>s\_j$$，则为1
  * 如果$$s\_i\<s\_j$$，则为0
  * 如果$$s\_i=s\_j$$，则为0.5
  * PyTorch中可以用 `(torch.sign(si-sj)+1.0)*0.5` 计算得到
* $$s\_i$$ 与 $$s\_j$$ 分别是项目i和j的输出分数
* 集合S中记录了所有需要计算的i，j对。

## 如果我们强行令$$s\_i>s\_j$$

如果我们强制$$s\_i>s\_j$$（如果$$s\_i\<s\_j$$的话就交换，且不计算相等的pair）。 那么我们得到一个简单的Loss：

$$
L\_{\omega} = - \sum\_{i,j \in S}{log(sigmoid(s\_i-s\_j))}
$$

### PyTorch的实现

```python
import torch.nn
import torch.nn.functional as F

def ranknet_bce_loss(diff_output: torch.FloatTensor, weight: torch.FloatTensor = None):
    """
    Calculate the loss of rncf with weight, and reduce by mean()
    We assumed that all the output is positive (1)
    :param diff_output: The value of net(x1)-net(x2)
    :param weight: The weight for each sample
    :return: Loss of ranknet
    """
    y_loss = -F.logsigmoid(diff_output)
    if weight is not None:
        return torch.mul(y_loss, weight).mean()
    else:
        return y_loss.mean()
```

## 如果我们采用矩阵计算加速

考虑一下，假设某个用户（或者query）有N个item，如果我们计算除了某个用户的所有的item的分数，那么Loss的计算就如

$$
L\_{\omega} = - \sum\_{i=1}^{N}{ \sum\_{j=1}^{N}{ t\_{ij} \times log(sigmoid(s\_i-s\_j)) + (1-t\_{ij}) \times log(1-sigmoid(s\_i-s\_j)) } }
$$

注意：原则上来说，只要计算不含对角线的下三角矩阵就可以了，也就是j从i+1开始计算。损失函数应该是对称的。 但是这里为了在numpy或者pytorch等框架下矩阵比循环快，且可读性好出发，所以这里j从1开始计算。

### PyTorch的实现

```python
import torch.nn
import torch.nn.functional as F

def ranknet_loss(
        score_predict: torch.Tensor,
        score_real: torch.Tensor,
):
    """
    Calculate the loss of ranknet without weight
    :param score_predict: 1xN tensor with model output score
    :param score_real: 1xN tensor with real score
    :return: Loss of ranknet
    """
    score_diff = torch.sigmoid(score_predict - score_predict.t())
    tij = (1.0 + torch.sign(score_real - score_real.t())) / 2.0
    loss_mat = tij * torch.log(score_diff) + (1-tij)*torch.log(1-score_diff)
    return loss_mat.sum()
```

## 如果我们直接计算梯度

这一次我们优化到直接计算梯度，对，就连Loss我们也不计算了，直接计算梯度优化。 之所以要计算梯度不是因为少一步能快点，而是我们可以直接针对输出的$$s\_i$$这样的评分输出进行优化。 这样优化可以大大减少网络前馈计算的次数，假设每个用户有N个item，那么我们就可以把前馈从$$N^2$$减少到N次。

$$
\frac{\partial{L\_{\omega}}}{\partial{\omega}}= \frac{\partial{L\_{\omega}}}{\partial{s\_i}}\frac{\partial{s\_i}}{\partial{\omega}} + \frac{\partial{L\_{\omega}}}{\partial{s\_j}}\frac{\partial{s\_j}}{\partial{\omega}}
$$

这里有两个结果列出，第一个是论文里的结果(sigma先取1好了)：

$$
\frac{\partial{L\_{\omega}}}{\partial{\omega}} = (\frac{(1-S\_{ij})}{2} - \frac{1}{1+e^{s\_i-s\_j}})( \frac{\partial{s\_i}}{\partial{\omega}} - \frac{\partial{s\_j}}{\partial{\omega}} )
$$

其中$$t\_{ij}$$和$$S\_{ij}$$的关系是：$$t\_{ij} = \frac{(1+S\_{ij})}{2}$$

我个人是使用$$t\_{ij}$$推导了一遍，结果是一样的，形式不一样，不相信的可以自己展开看。

$$
\frac{\partial{L\_{\omega}}}{\partial{\omega}} = (sigmoid(s\_i-s\_j)-t\_{ij})( \frac{\partial{s\_i}}{\partial{\omega}} - \frac{\partial{s\_j}}{\partial{\omega}} )
$$

这样我们就看到，针对 $$\omega$$ 的Loss的导数其实有两部分，一部分针对项目i，一部分针对项目j的。

不妨把前面的部分叫做$$\lambda\_{ij}$$，这也是论文的核心思想的前戏。 先整理一下：

$$
\lambda\_{ij}= \frac{(1-S\_{ij})}{2} - \frac{1}{1+e^{s\_i-s\_j}}= sigmoid(s\_i-s\_j)-t\_{ij}
$$

我们可以看到，对网络整体的导数可以被分解为对每一个项目的导数。

$$
\lambda\_{i}= \sum\_{j: {i,j}\in I}{\lambda\_{ij}} - \sum\_{j: {j,i}\in I}{\lambda\_{ij}}
$$

这里的意思是，把所有的$$pair\_{ab}$$里面$$a=i$$或者$$b=i$$的挑选出来，如果i在前面，那就取正，否则就取负。 全部加起来以后，就是对这一个分数项的梯度了。

### 最终版本的PyTorch的实现

```python
import torch.nn
import torch.nn.functional as F

def ranknet_grad(
        score_predict: torch.Tensor,
        score_real: torch.Tensor,
) -> torch.Tensor:
    """
    Get loss from one user's score output
    :param score_predict: 1xN tensor with model output score
    :param score_real: 1xN tensor with real score
    :return: Gradient of ranknet
    """
    sigma = 1.0
    score_predict_diff_mat = score_predict - score_predict.t()
    score_real_diff_mat = score_real - score_real.t()
    tij = (1.0 + torch.sign(score_real_diff_mat)) / 2.0
    lambda_ij = torch.sigmoid(sigma * score_predict_diff_mat) - tij
    return lambda_ij.sum(dim=1, keepdim=True) - lambda_ij.t().sum(dim=1, keepdim=True)
```


# 强化学习相关博客


# Re：从零开始的Multi-armed Bandit

## 前言

这是一篇旨在从零开始介绍多臂老虎机（Multi-armed Bandit）算法，有点综述性质的文章。为的是在本人死前说清楚其中的Stochastic Bandit问题和几乎所有的流行的解决方案。

而写这篇文章的原因是，疫情期间猫在家里实在是太无聊了，更重要的是最近尼玛在咳嗽，一周了没见好，也不发烧，也不知道是不是新冠，只能在惶恐的情绪下写完这个文章，心里想着搞不好这就是最后一篇文章了，再不写就写不动了。作为一个鸽产作者，这是一个让人心碎的事情。

如标题所见，这是一篇有点新人向的文章，但是即使你已经烂熟Stochastic Bandit系列算法，也不妨过来扫读一下，这里一些算法我会给出自己的理解。

虽然是新人向，但是最好还是有点概率论和线性回归（尤其是岭回归）的预备知识，否则看起来会有点痛苦。

## 啥是Multi-armed Bandit

想要知道啥是Multi-armed Bandit，首先要解释Single-armed Bandit，这里的Bandit，并不是传统意义上的强盗，而是指吃角子老虎机（Slot Machine）。按照英文直接翻译，这玩意儿叫“槽机”（这个翻译也是槽点满满），但是英语中称之为单臂强盗（Single-armed Bandit）是因为即使只有一个手臂（摇把），它也可以抢走你所有的钱。中文“老虎机”的老虎，也是这个意思。

尽管号称强盗，但是这个机器还是会给你一些收益，而不同的老虎机，特性也不同，如何快速找到吐钱最多的老虎机——或者说，如何在玩多个老虎机的时候取得最大的收益，这个问题，就是Multi-armed Bandit的原型。

## Stochastic与Non-stochastic

这一下就可以把Bandit问题分成两大类。

Stochastic是随机的意思，但是这个随机和Random又稍微有些不同，是有章法的随机。换言之，收益服从某一个确定的分布。例如抛硬币的游戏里，收益服从伯努利分布，或者我们可以设计一个收益服从高斯分布或者指数分布的游戏。。。。

而Stochastic Multi-armed Bandit 还有一个假设就是没有外部信息，一旦引入外部信息，我们就称之为Contextual Bandit了，就是有上下文的Bandit。

我们今天主要介绍的就是Stochastic Bandit，同时作为UCB算法的延伸，我们会介绍一下Contextual Bandit里面的LinUCB算法。

## Stochastic Bandit

Stochastic Bandit的核心就是这个公式：

$$
{\overline{R}}*n=n\mu^\* - E(\sum^{n}*{t=1}{\mu\_{I\_t}})
$$

关于这个公式，我可以给出一张图来说明：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-30566902ae3981aa4e5e9449a4cf033a369893cd%2F2020-05-05-19-15-01.png?alt=media)

这里使用的是Regret，也就是你距离理想状态的差距来定义。也可以用Reward，也就是你玩了多轮游戏以后的收益。

解决这一类问题，我们有很多种办法。

### 嘛也不干

对，躺着也是一种解决方法，所谓躺着，就是每次随便选中一个ARM，这种方法看上去一点效果也没有，作用会在与其它算法结合之后体现。 朴素

前K\_m轮我们嘛也不干，即平等的选择每一个臂，在K轮之后，我们永远选择平均收益最大的那个臂。你可以把前面的K\_m轮视作一个AB测试。

这种方法的问题在于，如果m选的太大影响收益，如果m太小了会导致严重的过拟合。

我在实验的时候就多次发现过拟合的存在（实际上根据实验的配置我可以准确的计算出过拟合的概率，哈哈哈）

### $$\epsilon-greedy$$

每次生成一个0～1之间均匀分布的随机数x，如果x小于 $$\epsilon$$ ，那就选择“嘛也不干”算法（探索），如果x大于 $$\epsilon$$，那就算则“朴素”算法的后半截——选目前最优的那个臂（利用）。

需要说明的是，这个算法看上去简单，实际非常有效，可以有效的避免过拟合的问题。

更重要的是，这个算法是我们人类大脑可以模拟的极限了，接下来的几种算法，如果不用计算机的话，执行起来会很困难。

所以如果有朝一日去赌场，请务必——别赌了，回来吧，赌场里都是骗人的（你以为我会劝你用算法？别天真了。。。）。

### Boltzmann Exploration

如果我们在每一步给每一个ARM赋予一个概率，这个概率由如下公式定义：

$$
p\_{i}^{t+1}=\frac {e^{\mu\_i^t/\tau}} {\sum\_{j=1}^{K}e^{\mu\_j^t/\tau}}
$$

那我们就得到了玻尔兹曼探索算法。这个算法有着比较厚重的物理背景，其中的 $$\tau$$ 表示的是温度常数，而 $$\mu$$ 表示的是这个摇臂的均值。

需要说明的是，这个算法我很喜欢，但是实际效果并不好，喜欢是因为他有物理背景，而讨厌是因为 $$\tau$$ 特别不好调整，经常容易不收敛或者过拟合。

一个解决方法是把 $$\tau$$ 定义成随时间变化的量。定义成一个单调递减的函数，这样就有退火算法内味儿了，但是烦恼的事情随之而来，之前我参数只要定义一个或者几个实数，现在倒好，要我直接给出一个函数。

当然我们可以继续照抄物理学里的温度变化的公式形式（指数下降），但是如果你看过铁碳合金相图就知道，温度下降的速度也会影响最后的材料品质，同理，温度下降的参数也会影响算法的效果，实在过于复杂，这里就放弃了。

### Thompson sampling

#### Beta分布

在介绍汤普森采样之前，我们首先要介绍Beta分布。记得第一次接触Beta分布的时候，直接给我搞懵逼了，他会和你说Beta分布是吉布斯（Gibbs）分布的一个特例，然后就会陷入吉布斯分布和采样的大坑。

其实Beta分布没有这么复杂，Beta分布最初用来描述抛硬币游戏中正面出现概率的分布。

举个例子，我抛了2次硬币，都是正面，然后我就说，这个硬币正面的概率是100%——这显然不合适。所以，这个时候我们需要一个分布来描述这个概率。同时，这里还有一个挑战，叫Conjugate Prior，共轭先验，换句话说，我们要求先验分布和后验分布有同样的形式。

我们就想到了，既然硬币的分布是服从伯努利分布： $$p(x)=p^x(1-p)^{1-x}$$ ，那么我们直接把它“堆起来”就能得到Beta分布的形式：

$$
Beta(\mu|P,N)=\frac{\mu^{P-1}(1-\mu)^{N-1}}{\int^1\_0{\mu^{P-1}(1-\mu)^{N-1}}d\mu} =\frac{\Gamma(a+b)}{\Gamma(a)\Gamma(b)}\mu^{P-1}(1-\mu)^{N-1}
$$

说实话，我喜欢前面一种描述，因为下面那一坨或者Gamma函数那一坨，只是为了让函数的积分为1。

这里的P，N分别是正面和反面的次数。

#### 正式进入汤普森采样

我们现在给每个ARM都有了一个Beta分布，那么我们针对每个ARM进行一次采样，就是依据这个ARM的Beta分布生成一个随机数，随后挑选数字最大的ARM。

对比上面的Epsilon Greedy，我们可以看到，这样的定义更加自然一点，从问题的分布定义直接一路推到如何采样，在数据累计的过程中，我们对均值估计的置信度也在变化，而汤普森采样很好的抓住了这种置信度的变化。整个公式没有一个自由参数，这是我最喜欢的地方。

不过缺点就是：只能够用于硬币问题，也就是Reward必须是0或者1。

说到抓住均值的置信度的变化，我们必须要介绍UCB算法了。 Upper Confidence Bound

这里的UCB是置信度上确界的意思，之所以是Upper是因为我们在针对Reward进行分析，如果你的问题用的是Regret的话那就是Lower Confidence Bound，采用的是下确界。

UCB算法讲究的就是一个乐观，也就是我尽可能的相信这个ARM能做到它的上确界，举个不恰当的例子，同样一个青年和一个中年人，他们的智商是一样的（均值一样），这个时候我倾向于选择青年，因为青年的不确定性更大。

这里要说，青年的不确定性，既有向上的部分，也有向下堕落的部分。这个青年可能成为一个人才，也可能成为一个赌棍流氓。那么我们为什么选择青年呢？这就是UCB算法的乐观所在，它乐观的相信每一个不确定性高的ARM一定能达到该不确定范围内的最大值。正是这种乐观，所以UCB最后基本都能收敛到最优。

那么我们怎么评估这个上确界呢？

这里要用到一个叫hoeffding inequality的东西：

$$
P{|\overline{x}-E(\overline{x})|\le\delta} \ge 1-2e^{-2n\delta^2}
$$

我们可以定一个p，这个 $$p=1-2e^{-2n\delta^2}$$ ，例如0.95或者0.99吧，随后我们可以计算出这个 $$\delta$$ ，这个就是上确界到均值的距离。

那为什么这个 $$\delta$$ 和p有这样的函数关系呢？本质上还是大数定律惹的祸，仔细看看，是不是发现这个函数形式是Gaussian分布的形式？

而随着样本的累积，n不断增大，这个确界就会慢慢的收紧，就好像随着我们个人的发展，我们的人生的可能性也在不断减小（当然很多时候减少的只是上确界，堕落还是没有底线的，这真尼玛是个让人悲伤的故事）。

好了，我们现在可以推出上确界的公式了：

$$
U\_{i} = \mu\_i + \sqrt{\frac{2lnT}{n}}
$$

本质上，后面那一坨就是p的逆函数，这样就不难理解为什么UCB公式是这个亚子了。

### LinUCB——Make UCB Great Again

如果硬要说这么完美的UCB算法有什么缺陷的话，那就是不能利用外部信息，而推荐系统里面是有外部信息的——用户的ID，Embedding，Item的关键词，Embedding之类的。。。

就算是吃角子老虎机也有型号啊，大小啊之类的是不是？

那如何利用这些信息呢？

我们可以考虑用线性回归来学习这些内容，同时我们得想个办法干他一炮——不是，得想个办法估计一下上确界。

首先我们可以得到UCB里的均值的公式：

$$
E(r\_{t,a}|x\_{t,a}) = {x\_{t,a}}^T\theta\_a^\*
$$

其中 $$x\_{t,a}$$ 就是我们拿到的上下文，这个上下文一般是User和Item的组合特征，而 $$\theta\_a^\*$$ 是我们要学习的参数。

很明显的线性公式，那么参数估计就可以用这个岭回归：

$$
\theta\_a^\*=({D\_a}^TD\_a + I\_d)^{-1}{D\_a}^Tc\_a
$$

如果你没印象了，可以去翻翻PRML第三章。

重点来了，文中给出估计上确界的方式是：

$$
b\_{t,a} = (1+\sqrt{\frac{ln(2/\delta)}{2}})\sqrt{ {x\_{t,a}}^T({D\_a}^TD\_a + I\_d)^{-1}{x\_{t,a}} }
$$

其中概率 $$p=1-\delta$$

我们怎么理解这个公式呢？按照我在后面给出的参考文件里面的方法去推也可以，或者听一下我的这个理解：

我们知道，线性回归除了可以从最小均方根误差求偏导为0导出，还可以由残差服从正态分布，然后做极大似然估计导出。

知道了这些，你再看看 $$\sqrt{\frac{ln(2/\delta)}{2}}$$ 长得像不像正态分布的逆函数形式？而 $${x\_{t,a}}^T({D\_a}^TD\_a + I\_d)^{-1}{x\_{t,a}}$$ 中间难道不是协方差矩阵吗？

当然我这么理解不严谨，如果你追求严谨的理解，请去看下面列出的论文。

## 杂谈

一口气介绍了这么多算法，下面说一下一些闲言碎语。

我喜欢没有参数，或者参数有具体的物理意义，数学意义的算法，比如Boltzmann Exploration，UCB和汤普森采样都是我心目中理想的算法的样子。理论看上去很完美，但是在生产中还有更多的问题，比如如何把商业指标和你的Reward联系在一起？在实验中你可以立刻得到反馈的Reward，如何处理无法立刻得到Regret或者Reward的情况？UCB固然很美好，但是所有的拓展都要求计算上确界，理论分析是个很麻烦的事情，有没有什么方法可以模拟出一个哪怕不是很精确的上确界？

## 参考文献

首先推荐这两篇论文：

1. [Regret Analysis of Stochastic and Nonstochastic Multi-armed Bandit Problems](https://arxiv.org/abs/1204.5721)
2. [Introduction to Multi-Armed Bandits](https://arxiv.org/abs/1904.07272)

与其叫论文，不如叫教科书，你看过哪篇论文里他娘的有课后习题的。

这两本书的叙述结构稍微有点不同，但是理论都是一样的严谨，换句话说，不是很好理解，我也没有全部读完，但是，读blog这种非正式读物（比如我这篇文章）的时候，你会期待有一个词典一般的书供你查阅。

关于UCB里面用到的不等式，其实是大数定律的进一步扩展，这里我们可以阅读这篇老的掉牙的文献：

[Probability Inequalities for sums of Bounded Random Variables](https://link.springer.com/chapter/10.1007/978-1-4612-0865-5_26)

虽然老的掉牙，这篇文章不可谓不硬核，希望各位谨慎入内，到时候不要哭着关闭浏览器。

[A Contextual-Bandit Approach to Personalized News Article Recommendation](https://arxiv.org/abs/1003.0146)

上面这篇就是LinUCB的原文了，但是这里面主要介绍了怎么应用，没有介绍怎么能推导出LinUCB的公式内容，这里有篇硬骨头告诉你公式是怎么来的，我已经有点知难而退了。希望各位迎男而上：

[Exploring compact reinforcement-learning representations with linear regression](https://arxiv.org/abs/1205.2606)

如果你不满足于线性模型带给你的快感可以尝试一下非线性模型：

[Finite-Time Analysis of Kernelised Contextual Bandits](https://arxiv.org/abs/1309.6869)

这篇文章就在LinUCB的基础上引入了核方法，换句话说，用SVR（支持向量回归）的哪一套理论替代了岭回归。

先写这么多，祝各位身体健康。


# 技术杂文

一个工程师做过的难以分类的工作，和一些技术相关的碎碎念。


# Jeff是如何被同事发明的DSL残害的

以下都是Jeff的经历，和作者本人不能说是一点关系没有吧只能说是没有一点关系。

大概在几年前，我就读过一篇文章：[不要以 DRY 之名，发明低代码 DSL 去残害你的同事](https://zhuanlan.zhihu.com/p/357411780) [互联网备份链接](https://archive.vn/IdYQ9)

当时的我还是年轻，低估了这篇文章的威力。当然，有能力做一个DSL出来的人也不多，而且我们那个时候的场景也没有那么多变，所以并没有这样的问题。 我当时也用django开发项目，对于django的“最后10%”问题也深有体会，经常为了实现一些非标的功能绞尽脑汁。话虽如此，却也切实体会到了django的便利之处，所以虽然对文章赞同，但是觉得有点言过其实。

直到Jeff（再次免责声明，不是我，哈哈哈）遇到了一些和DSL有关的奇妙经历。

## Jeff遇到了什么问题

Jeff的公司有很多相似的业务逻辑。比如从A系统，B系统取出数据，按照一些业务逻辑整合后再送到C系统中去计算，计算的结果返回去或者送到D系统中做更多处理。以前这些系统大多是Go写的，有一个神人决定整合这一套系统，于是做了一套复杂的YAML配置，通过配置文件就可以决定数据被如何处理，每一个处理背后都有Go语言写的处理代码来对应。于是大多数的业务逻辑都可以用一个YAML表示。如果处理方法不够就新增处理方法。

虽说是配置文件，我觉得其复杂度和DSL也差不多了。

很快，这个文件就复杂到人类很难看懂了。里面定义了各种各样的数据源、合成器等等，但是没人知道它们会如何作用。 这个时候另一个神人发现：“这不就是定义了一个DAG嘛！我去，不早说！”

于是写了一套DAG运行器。简单来说就是定义了许多Operator，有的可以取数据，有的可以送到推理服务器去计算，中间的变换逻辑Operator（或者说胶水）就让DuckDB的SQL来承担。

用户可以定义很多的STAGE，定义它们之间的依赖关系。下游的STAGE可以用上游的STAGE的计算结果来进行进一步处理，最后经过各种处理之后，可以指定一个输出STAGE，这个STAGE的输出数据（一张表）就会作为返回值。

### 一个很有洞见，但是并不好的方案

必须承认，DAG的方案比第一种方案已经好很多了，至少DAG有清晰的结构和执行顺序，比起第一种的“定义了，但是不知道什么时候会被如何执行”要好得多。

而且这种“原来都是DAG”我认为是非常深刻的洞见，基于这样的理解，是能够做出好产品的。可是，可是！这样的DSL仍然折磨着每一个人。

因为使用这个DAG系统本身有代价，这个代价和DSL的代价是一样的。

## 一个DSL做出来了，那么代价呢？

其实写一个DSL和开发一门新的语言没有太多的区别，要考虑DSL的开发，调试，监控（Tracing/Profiling），执行效率反而是最后需要讨论的一环。

就以Jeff遇到的问题为例，他遇到的DSL是YAML（也可以用JSON，反正能用yq/jq互相转换，所以都一样，包括JSONNET，下略）格式内嵌一部分SQL。我们一个个看：

### 开发

大抵现代的文本编辑器都是支持YAML编辑的，这一点问题不大。至于符合不符合自定义的某些规范，K8S也有珠玉方案在前，其实YAML的验证、补全方案大概是没有问题的（虽然本案例中也没有做就是了）。

SQL的补全本来是有现成的方案的，但是如果和YAML混合到一起就比较麻烦了，YAML对内嵌的文本的编辑做得不是很好。

即使我们克服了这一点，想要有一个顺畅的开发流程，还需要解决另一个问题，就是上下文。 每一个不同的STAGE它能访问的数据表也不一样，表结构也各不相同，上下文当然也不一样。 一个好的开发工具，在代码编写中就要能静态解析出当前DAG能够访问哪些上游的数据表及其表结构来做自动补全和报错。

然而这些目前也都没有。就是靠文本编辑器+最近很流行的Generative AI硬来。😅

### 调试

其实其他类型的Stage还好说，最大的问题出在SQL上，SQL的执行中出问题，可能是本身SQL有问题，也可能是上游Stage输入的数据有问题。

首先SQL本身就不适合调试，写过WITH很多个临时view的大型SQL的人应该有所体会，我们往往是需要一个个地去验证里面的小View是不是输出了正确的结果，像建造金字塔一样地把SQL垒起来。而SQL写好以后，就很难调试了，这个时候如果说中间某个view的输出有问题，那就只能拆了然后重新垒。

即使我们忽略SQL的这个问题（因为有DAG可以把大SQL拆开），一个比较顺畅的开发流程也应该是：准备各个上游的Mock的数据（表），然后单元测试去执行SQL，肉眼或者写一些脚本来验证输出是不是正确。这些原方案都没有做。在SQL执行出错的时候，一个完整的报错（StackTrace）应该包含哪个DAG的哪个Stage出错了，出错的原因是什么，上游已经完成的Stage的输出和输入都是什么。但是或许是为了数据安全，现在也没有支持这一点。

### 监控

这一点Jeff用的那个系统做得比较好，其实DAG天然适合被Opentelemetry做Tracing。 默认所有的环节都做Tracing比在Go里面一个个添加要方便一些。

但是Profile没有做，导致线上性能出现问题以后只能看最近的Tracing的记录去感觉+猜。

### 性能

其实性能是最后解决的问题，因为大多数的服务没有那么多的QPS，QPS过低造成的Connection问题其实比QPS过高造成的Latency问题还要普遍一些。实在遇到了问题可以靠堆机器解决。但是这个DAG运行器的性能也确实是问题，Jeff有一次遇到了一个要求3000QPS的服务，DAG又比较复杂，单单是300QPS就可以吃掉30Core的CPU，也就是一个CPU Core才能扛10QPS。

性能问题的根源也很简单，SQL的解析需要CPU，更何况是每一次Request都会解析一次SQL。哪怕是解析后，SQL的执行效率也不如机器代码或者Java字节码，CPU不是设计来干这个的。当然，这一部分我觉得DuckDB是有解决方案的。

或许以后可以有方案把SQL的Plan预编译成类似字节码的东西，然后执行就会快很多，但是这样调试可能就更麻烦了。

总而言之，现在的方案造成了资源的极大浪费。如果一开始就用Go语言写，一开始的时候或许会失去并行执行的速度优势（当然用chan可以实现，只是写起来也并不优雅），但是最终效率应该是更高的，而且只需要几个Core的CPU和几百M的内存就可以承受数千的QPS。如果用Rust效率可能更高（但是开发效率可能下降）。

## 代价

我们可以看到，其实上面每一个课题都是需要花大功夫的。日常的开发、调试方面，我们之所以有那么多的IDE，就是为了方便地开发某种语言，比如Goland来开发Go，Pycharm来开发Python。即使都用VSCode，至少你也要做一个LSP Server来接入，这些都是要花大力气去做的。而开发DSL的人往往有意无意地忽略了这个问题，大多是写完这门语言以后就不管了……于是除了作者会写这门语言，其他的人写起来都非常的痛苦。

## 不用DSL，那我们用什么？

是啊，用什么？（震声）就他妈的用Python，用Go，用Rust，甚至用C啊！给这些语言写库，然后用这些语言当胶水把你写的库以业务逻辑想要的形式粘起来。

这些大范围被使用的语言，本身调试器，工具链（Tracing，Profiling）都是完备的，有什么理由非要自己造轮子呢？

我们挑个性能最差的说好了（就是你了，Python）。

比如DAG，Python中完全可以用async/await语法来完成并发，Python中有丰富的类型（比如字典）也不至于一个JOIN就浪费很多资源，更重要的是，开发的时候可以用pdb之类的工具进行调试，上下文，当前的StackTrace，都一清二楚，为什么非要写SQL呢？

而且哪怕是Python，只要设计得当，性能其实肯定是比SQL要强的，至少人家开始就编译成字节码了，用不着每次都去Parse AST。

## 总结：复杂度不会消失，只会转移

恰如AI无法解决复杂度的问题一样，DSL更是解决不了复杂度的问题。

DSL的适用场景是：

1. 业务逻辑块高度重复且限定，而且以后新增的可能性较少
2. 对灵活度的要求极高，比如要求随时能够新建一个业务逻辑（有大量不懂编程但是可以被简单培训的技术人员）

除此之外的场景，请不要发明DSL去残害你的同事。


# Linux下使用任天堂手柄的体验

家里确实也是买了Nintendo的Switch，但是买来以后猛猛玩了几个月以后就吃灰至今。

手柄倒是买了不少。之前为了打双人游戏买了一个手柄，大概长这样：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-658461a38811cb273d0ee64cf640643f22e0d12b%2F2026-08-30-00-48-38.png?alt=media)

之后发现没有震动，加之也没法和家人玩马里奥游戏，趁着回国在淘宝又买了一对。

这些手柄如今并没有什么用，卖掉又有些可惜，好歹上面有摇杆、各色按钮、陀螺仪和加速度传感器，是个绝好的输入设备，于是乎考虑做点什么。

## 如何连接和配置手柄

### 连接

只要按住上面按钮就会进入配对模式：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5d6953dad350c64d5b416017585f247188494a03%2F2026-08-30-00-59-08.png?alt=media)

按着按钮然后再搜索蓝牙设备就可以把手柄连上去。

### 配置

用的是[AntiMicroX](https://github.com/AntiMicroX/antimicrox)，这是一个有图形界面的配置工具，可以方便的把各个按键映射为鼠标和键盘的操作。

我的系统是Debian13，GNOME(wayland)，安装方式选了flatpak。

按照官网的说明安装即可，安装好以后启动软件即可在GUI配置：

```bash
flatpak run io.github.antimicrox.antimicrox
```

由于我是Wayland不是X11，模拟鼠标和键盘也不如之前那么自由，比较通用的做法是新建一个uinput来模拟一个输入设备。这个时候可能会遇到错误，参照[这个页面](https://github.com/AntiMicroX/antimicrox/wiki/Open-uinput-error)修复即可。

```bash
sudo mkdir -p /etc/udev/rules.d
cd /etc/udev/rules.d
sudo wget https://raw.githubusercontent.com/AntiMicroX/antimicrox/master/other/60-antimicrox-uinput.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
```

配置界面大致如图：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4c37c645b04aecf24d353cf9fab0bce42b243b62%2F2026-08-30-01-19-41.png?alt=media)

flatpak安装多少还是有点问题，比如：

* 界面没有按照我的系统设定变成暗色主题
* 无法最小化，最大化也有些小问题

之后我可能试试看安装deb，但是我个人对这种方式稍微有些抵触。

## 最终我拿它干了什么

嗯……干了两件事情。

第一件还是打游戏，我在Debian上安装了虚拟机，安装了Windows系统玩小时候玩过的空中攻击（Airstrike）游戏。

我这个人马大哈，魂经常不在身上。硬盘也是摔了好几块，小时候写的代码啥的都没有了，但是这个游戏（连带记录？）都完好的保存下来了。

这里的就单纯是把手柄当成一个键盘用了。

当然也不是不能用重力加速传感器来操作，但是我觉得那反而让我不好操作。

第二件是就是当成遥控板，吃饭的时候我会把电脑接到餐桌边的屏幕上看看B站网飞Youtube什么的。但是操作还得靠电脑有点不便。以后可以合上电脑直接操作手柄了。

## Linux驱动任天堂手柄的基本原理

我也不是Linux驱动开发的专家，所以只是把我调查到信息稍微说个一二。如果有错请联系我。

### AntiMicroX的原理

简单来说，就是把SDL2进行一个封装，SDL是一个C语言写的，跨平台的库，有了它就可以直接用API获取设备或者轮询操作。AntiMicroX用的是C++，但是我搜索了一下，也是有[Python的SDL库](https://github.com/py-sdl/py-sdl2)的，之后考虑可以做些什么玩具玩玩。

所以有了SDL以后可以把跨平台的一些脏活全部丢给它，基于SDL的API去做一些上层的配置。话虽如此，我不是要轻视AntiMicroX的开发。 我清楚界面开发的工作还是极为繁重的，AntiMicroX的界面做的极其简单易懂，特别是摇杆和传感器的部分，直接能看到数据和状态，让我对手柄的理解更加深了（感谢开发者的无私奉献）。

通过SDL获取了手柄的各个数据以后，下一步就是根据配置去输出。我是Wayland，所以这里走的就是uinput的路子。通过 /dev/uinput 模拟出一个一个标准的键鼠输入设备，然后对其发送信号即可。

### SDL2是怎么做的呢？

其它平台我没有深究，我只看了Linux的部分。Linux直接会提供标准的事件设备然后生成/dev/input/eventX文件。SDL直接去读取这些事件即可。SDL还支持别家的手柄，会将所有手柄的数据转为统一的控制指令，刚才说的AntiMicroX的数据就是从这里来的。

### hid-nintendo驱动的原理

令我诧异的是，任天堂的手柄设备的驱动居然[写入了Linux内核](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-nintendo.c)！

我以为像这样的设备多少得是私有的协议所以做成DKMS之类的（类似英伟达）。我觉得这不符合N社的尿性。

后来我查了一些资料，别说DKMS了，任天堂啥也没有提供。不愧是你啊，任天堂。我还是Too young too simple, sometimes naive. 我买的是HORI的手柄，是任家官方认可的设备，肯定是又内部的资料的。我看到资料说有些设备则是完全靠逆向来生产的，我只能说还是有狠人。

所以这个驱动完全是大家通过逆向工程搞出来的产物（感谢大佬们）。

`hid-nintendo`驱动的任务是，将蓝牙发送的HID报文转换为（SDL库需要的）事件。所以驱动的开发就需要完整的报文协议，这个协议毫不意外是私有的。

好在这个世界永远不缺有时间的大神，[dekuNukem/Nintendo\_Switch\_Reverse\_Engineering](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering)上面就有完整的报文协议。里面有很多有意思的细节，比如比如摇杆的精度是12bit，这样XY轴的数据就可以塞到3个Byte里面。

除了解析协议以外，这个驱动还有一些其它任务，比如根据读出的出厂时候的纠偏数据来对传感器进行校正。（可是我觉得这些应该在手柄内完成，难道是为了功耗？）

## 所感

总结一下，从数据的流向看，工作的机制是手柄（蓝牙）-> hid-nintendo报文解析 -> SDL事件读取和标准化 -> AntiMicroX配置按键映射 -> （至少对于Wayland）uinput模拟输入设备。

我只是简单的在我的电脑上敲了几行Bash命令，然后用鼠标点击配置了一下按键映射，就打开我的游戏来玩了。 但是背后是一层又一层的信号传递，是一个个大神破解协议，开发驱动，开发标准库，开发界面。经过了这么多的接力棒，把简单易用的解决方案传到了我的手上。

与任天堂的顽固和封闭相比，我更喜欢开源、开放的这种勃勃生机万物竞发的境界。


# 使用Termux把Android变成一台服务器

## 起因

我之前的Pixel4因为电池鼓包了，把手机的背板微微顶起来了。顶起来以后NFC接触不良，所以我发现了。换电池的时候，可能我手上的静电打坏了什么东西，反正信号差了，前置摄像头也不能用了。就这样，这个手机在我的柜子里吃灰了几年。

最近我冒出了一些俺寻思之力：既然Android的底层也是Linux，虽说这个Linux和我们日常使用的Debian之类的发行版又有少许不同，那么能否让Android成为一台服务器呢？

诶！还真行。站在Termux巨人的肩膀上，几乎没什么要做的事情。

但是也有一些限制，这里整理一下。

## 如何操作

### 安装Termux

首先是操作系统，我的操作系统是CalyxOS 5.14.2，一个开源的、真正自由的Android。里面没有Google原装Android那么多鬼东西。 我之所以用它，是因为几年前我刷机刷了它，懒得刷回去了。再者，我也更喜欢这种简洁干净的系统。

不过如果只是运行Termux的话，相信别的Android系统应该也基本都可以安装。不一定要使用CalyxOS。

我用F-Droid安装了Termux和Termux:Boot两个App，一个是核心，一个负责开机启动。

#### 多余的话

这里我得为F-Droid（和任何期待自由的人）来『广告』一下，Google即将在Android实施限制，任何Google认证的Android设备安装的App，其开发者必须在Google缴费注册。

这也意味着，想要自己写一个apk分发到世界各地，不太可能了。 当然，Google假惺惺的提供了绕过限制的方法，但是操作路径之复杂，还要等24小时（等我们自己忘了操作吗？），让分发应用受到极大的限制。 而且Google也可以任意修改这些规则。

我们这些用户，只是想要在自己买的设备上安装任何自己想要的应用，却要费千辛万苦。

而且我不负责任的说，垄断收费只是最小的副作用。这个功能给了各国政府（中国是个例外，中国基本没有Google认证Android，是以另外的方式——比如反诈中心——来控制）以屏蔽某个应用的手段，以后就可以以“国家安全”的名义来封禁任何不想要的应用，对于许多国家来说，这会强化独裁。

更多详细信息，请看[Keep Android Open](https://keepandroidopen.org/)

### 连接到手机

```shell
pkg update
pkg upgrade
pkg install openssh tmux git vim zsh # 安装一些常用的应用
```

如果下载慢，可能需要你`termux-change-repo`来换个源。

然后我们运行`whoami`获取当前用户，比如我是`u0_a219`。这里是个有点神奇的地方，这里的u0应该是指用户的编号，我这个系统当然支持多租户，但是我只创建了一个用户。a219则是指app+编号。

随后我们可以新增一个authorized\_key来进行ssh的登录，也可以先设置密码通过密码登录。这部分和标准的Linux一样我就不再赘述了。

唯一需要注意的是，请使用sshd启动服务器。

重启sshd的方式是

```bash
pkill sshd
sshd
```

### 和一般Linux的区别

路径不一样，因为我们只是有用户的权限，所以文件夹也是`/data/data/com.termux/files/usr`而不是挂在根目录下。手机存储的路径在`/storage/emulated/0/`，这个老Android用户肯定都熟。

其次也没有systemd这种东西，所以服务器是作为一个正常的App来运行的。比如我之后运行Prometheus，就是直接用二进制启动的。

开机启动请编辑 `~/.termux/boot/start-server` 里面写

```bash
#!/data/data/com.termux/files/usr/bin/sh
termux-wake-lock
/data/data/com.termux/files/usr/bin/sshd
# 别的有的没的。。。
```

申请端口开放的时候只能申请8000以上的端口，不过一般都够用了。

最大的区别其实是glibc没有了朋友！Android的C库是Bionic。至于为什么要费劲搞一个C库，只能说glibc的历史包袱太多了。而且Android本身也不是服务器，对userspace的要求也不一样。

可是大多数标准的Linux App（特别是C/C++开发的那些）都会用到glibc，所以它们大多得重新编译。

但是自带Runtime的语言就没事了，比如你可以安装Python，Golang，rust，前者是解释器，后面两个则可以跨平台编译，只要你不是调用了C库，事情就很简单了。

但是考虑到大多数软件都不会闲到构建一个Android的版本，很多时候还是得自己编译，比如我使用pipx安装uv的时候，就得重新用rust编译uv，rust的编译工作量大家都懂，我觉得我的Pixel4都要冒火星子了。

#### 多余的话

* 也没有Root权限，所以docker这种东西是想都不要想（当然可以Root但是我只打算运行），如果有兴趣，请确认这个文档：[Docker on Android](https://gist.github.com/FreddieOliveira/efe850df7ff3951cb62d74bd770dce27)
* 如果你想要真正的 Debian 用户空间，可以试试`pkg install proot-distro`然后`proot-distro install debian` + `proot-distro login debian`，我这次是探索直接在Android下面运行App能到什么程度，不管这个。

### 跑Prometheus

为什么我决定用Pixel跑Prometheus呢？主要是下面几个原因。

* Prometheus完全用Go写成，Go有自己的Runtime，不依赖glibc，所以我不用重新编译（有空还是重新编译的好）
  * 顺便一提，编译的时候配合GOOS=android GOARCH=arm64即可。
* 我的手机的存储跑Prometheus正好，而且还是SSD，速度快。
* 手机自带电池，作为监控和系统，这很完美了。
* 最近我运行在别处的Prometheus有一点死了（主要是外接硬盘的寿命差不多了）

直接到官网下载给Linux ARM64准备的Prometheus的二进制文件，解压下来就能跑。要是没有Termux，我可能就得用gomobile先把Prometheus编译成AAR，然后再用Kotlin写一个简单的控制和配置界面了，那么会麻烦特别多。

之后我可能会把Loki之类的服务也放到这里。

### 安装uv

我也是没有特别期待它能运行多少Python的应用，因为很多Python的程序都有C扩展，安装要么慢，要么干脆不能安装。 但是uv用了rust的依赖，所以用来解释一下rust编译的问题是再合适不过了。

首先就是确保你的Termux里面安装了rust：`pkg i rust`

其次要设置正确的ANDROID\_API\_LEVEL，可以这么获取：`export ANDROID_API_LEVEL=$(getprop ro.build.version.sdk)`

### 还能干什么？

其实它可以作为一个很好的干杂活的服务器。由于缺失了systemd这些关键的系统组件，其应用还是受到了不小的限制。运行也不是很稳定，比如负载重的时候sshd都可能被挤崩。

话虽如此，我们都开始拿手机当服务器玩儿了，最重要的肯定是开心咯？谁会在乎可靠性和性能呢？


# 家用Airflow搭建指南

## 前言

本文会介绍一种使用Docker Compose + Git + S3启动一个完全可用的Airflow的方法和过程中踩的坑。

首先说说为什么需要在家里搭Airflow，正常好人家里是不需要Airflow的，但是我最近遇到了一些需要管理的定时任务。

所谓管理，也就是任务的定时启动、手动启动、历史日志（以及任务的状态）的管理和并发控制。

其中最痛的当属并发控制了。比如：

* 从IBKR下载数据的时候最多使用32个Client ID
* 使用yfinance下载数据的时候最多单线程，多线程就容易报429（Too Many Requests）的错误
* 迁移数据库表结构的时候最多只能有一个实例运行，否则容易乱套
* ……

我以前是怎么做的呢？ 定时任务用crontab，手动启动那就直接执行，每次启动自动用日期创建日志文件，历史日志则写了个脚本定期删除过期日志。 并发控制都是采用文件锁的方式完成这些任务的，写脚本费心费力不说，而且也不好管理。

我很早就知道Airflow，工作中也一直在用，那么为什么我的HomeLab不用Airflow呢？

当然是因为Airflow的配置本身也很难顶，特别是工作中的时候我们用的是K8S，更增加了部署的难度，给我留下了深刻的心理阴影。 这也就是为什么我前面说：“正常好人家里是不需要Airflow的”。

但是随着手动+脚本管理任务的难度逐渐高于配置Airflow的痛苦程度，我最终还是选择了Airflow。

## 最终成品

首先给出最终成品，你拿着这个脚本就可以自己运行了。

```yaml
x-airflow-common-env: &airflow-common-env
  AIRFLOW__CORE__EXECUTOR: LocalExecutor
  AIRFLOW__CORE__AUTH_MANAGER: "airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager"
  AIRFLOW__API__AUTH_BACKENDS: airflow.api.auth.backend.basic_auth
  AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${AIRFLOW_DB_USERNAME:-airflow}:${AIRFLOW_DB_PASSWORD:-airflow}@postgres/airflow
  AIRFLOW__CORE__LOAD_EXAMPLES: "false"
  AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: "true"
  AIRFLOW__DAG_PROCESSOR__REFRESH_INTERVAL: "10"
  AIRFLOW__DAG_PROCESSOR__MIN_FILE_PROCESS_INTERVAL: "10"
  AIRFLOW__CORE__MIN_SERIALIZED_DAG_UPDATE_INTERVAL: "10"
  _AIRFLOW_DB_MIGRATE: "true"
  _AIRFLOW_WWW_USER_CREATE: "true"
  _AIRFLOW_WWW_USER_USERNAME: ${_AIRFLOW_WWW_USER_USERNAME:-airflow}
  _AIRFLOW_WWW_USER_PASSWORD: ${_AIRFLOW_WWW_USER_PASSWORD:-airflow}
  AIRFLOW__WEBSERVER__EXPOSE_CONFIG: "true"
  AIRFLOW__API_AUTH__JWT_SECRET: ${AIRFLOW__API_AUTH__JWT_SECRET:-airflow_jwt_secret}
  AIRFLOW__API_AUTH__JWT_ISSUER: ${AIRFLOW__API_AUTH__JWT_ISSUER:-airflow}
  AIRFLOW__API__BASE_URL: https://airflow.your-domain.com
  AIRFLOW__LOGGING__REMOTE_LOGGING: "true"
  AIRFLOW__LOGGING__REMOTE_BASE_LOG_FOLDER: s3://airflow/logs
  AIRFLOW__LOGGING__REMOTE_LOG_CONN_ID: "airflow_log"
  AIRFLOW_CONN_AIRFLOW_LOG: ${AIRFLOW_LOG_CONN}
  AIRFLOW__CORE__FERNET_KEY: ${AIRFLOW__CORE__FERNET_KEY}

services:
  postgres:
    image: postgres:17
    container_name: airflow-postgres
    environment:
      POSTGRES_USER: ${AIRFLOW_DB_USERNAME:-airflow}
      POSTGRES_PASSWORD: ${AIRFLOW_DB_PASSWORD:-airflow}
      POSTGRES_DB: airflow
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "airflow"]
      interval: 10s
      retries: 5
      start_period: 5s
    restart: unless-stopped
    # ports:
    #   - 15432:5432 # For debugging only

  airflow-init:
    image: apache/airflow:${AIRFLOW_IMAGE_TAG:-3.2.2-python3.13}
    container_name: airflow-init
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      <<: *airflow-common-env
      AIRFLOW__CORE__EXECUTOR: LocalExecutor
      AIRFLOW__CORE__AUTH_MANAGER: "airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager"
      AIRFLOW__API__AUTH_BACKENDS: airflow.api.auth.backend.basic_auth
      AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://${AIRFLOW_DB_USERNAME:-airflow}:${AIRFLOW_DB_PASSWORD:-airflow}@postgres/airflow
      AIRFLOW__CORE__LOAD_EXAMPLES: "false"
      AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: "true"
      AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: "10"
      _AIRFLOW_DB_MIGRATE: "true"
      _AIRFLOW_WWW_USER_CREATE: "true"
      _AIRFLOW_WWW_USER_USERNAME: ${_AIRFLOW_WWW_USER_USERNAME:-airflow}
      _AIRFLOW_WWW_USER_PASSWORD: ${_AIRFLOW_WWW_USER_PASSWORD:-airflow}
    volumes:
      - ./data/dags:/opt/airflow/dags
      - ./data/logs:/opt/airflow/logs
      - ./data/plugins:/opt/airflow/plugins
    user: "${AIRFLOW_UID:-50000}:0"
    command: version

  airflow-apiserver:
    image: apache/airflow:${AIRFLOW_IMAGE_TAG:-3.2.2-python3.13}
    container_name: airflow-apiserver
    depends_on:
      airflow-init:
        condition: service_completed_successfully
    environment:
      <<: *airflow-common-env
    volumes:
      - ./data/dags:/opt/airflow/dags
      - ./data/logs:/opt/airflow/logs
      - ./data/plugins:/opt/airflow/plugins
    ports:
      - "8080:8080"
    user: "${AIRFLOW_UID:-50000}:0"
    command: api-server
    restart: unless-stopped
    healthcheck:
      test:
        ["CMD", "curl", "--fail", "http://localhost:8080/api/v2/monitor/health"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s

  airflow-scheduler:
    image: apache/airflow:${AIRFLOW_IMAGE_TAG:-3.2.2-python3.13}
    container_name: airflow-scheduler
    depends_on:
      airflow-init:
        condition: service_completed_successfully
    environment:
      <<: *airflow-common-env
      AIRFLOW__CORE__PARALLELISM: "16"
      AIRFLOW__CORE__MAX_ACTIVE_TASKS_PER_DAG: "8"
      AIRFLOW__CORE__MAX_ACTIVE_RUNS_PER_DAG: "4"
      AIRFLOW__SCHEDULER__MAX_TIS_PER_QUERY: "16"
    volumes:
      - ./data/dags:/opt/airflow/dags
      - ./data/logs:/opt/airflow/logs
      - ./data/plugins:/opt/airflow/plugins
      - /var/run/docker.sock:/var/run/docker.sock
    group_add:
      - "${DOCKER_GID}"
    user: "${AIRFLOW_UID:-50000}:0"
    command: scheduler
    restart: unless-stopped

  git-sync:
    image: registry.k8s.io/git-sync/git-sync:v4.7.0
    environment:
      GITSYNC_ADD_USER: "true"
      GITSYNC_GROUP_WRITE: "true"
      GITSYNC_REPO: "ssh://git@git.tsingjyujing.com:10022/homelab/airflow-dags.git"
      GITSYNC_BRANCH: "master"
      GITSYNC_ROOT: "/git"
      GITSYNC_LINK: "repo"
      GITSYNC_PERIOD: "30s"
      GITSYNC_DEPTH: "1"
      GITSYNC_SSH: "true"
      GITSYNC_SSH_KEY_FILE: "/etc/git-secret/ssh"
      GITSYNC_KNOWN_HOSTS: "true"
      GITSYNC_SSH_KNOWN_HOSTS_FILE: "/etc/git-secret/known_hosts"
    user: "${AIRFLOW_UID:-50000}:0"
    volumes:
      - ./data/dags:/git:rw
      - ./git-secret/id_airflow_ed25519:/etc/git-secret/ssh:ro
      - ./git-secret/known_hosts:/etc/git-secret/known_hosts:ro
    restart: unless-stopped

  airflow-dag-processor:
    image: apache/airflow:${AIRFLOW_IMAGE_TAG:-3.2.2-python3.13}
    container_name: airflow-dag-processor
    depends_on:
      airflow-init:
        condition: service_completed_successfully
    environment:
      <<: *airflow-common-env
    volumes:
      - ./data/dags:/opt/airflow/dags
      - ./data/logs:/opt/airflow/logs
      - ./data/plugins:/opt/airflow/plugins
    user: "${AIRFLOW_UID:-50000}:0"
    command: dag-processor
    restart: unless-stopped
```

配合.env文件：

```bash
AIRFLOW_UID=1000
AIRFLOW_IMAGE_TAG=3.2.2-python3.13
AIRFLOW_DB_USERNAME=airflow
AIRFLOW_DB_PASSWORD=your-db-password
AIRFLOW__CORE__FERNET_KEY=your-fernet-key
AIRFLOW_LOG_CONN='{"conn_type":"aws","login":"your-access-key","password":"your-secret-key","extra":{"endpoint_url":"http://your-endpoint:your-port"}}'
_AIRFLOW_WWW_USER_USERNAME=your-airflow-username
_AIRFLOW_WWW_USER_PASSWORD=your-airflow-password
AIRFLOW__API_AUTH__JWT_SECRET=your-jwt-secret
AIRFLOW__API_AUTH__JWT_ISSUER=airflow
DOCKER_GID=989
```

## 踩过的坑

### API Base URL

这个不设置好，跳转的时候会有问题：

```yaml
AIRFLOW__API__BASE_URL: https://airflow.your-domain.com
```

按照道理我应该放到.env中去设置，但是我太懒了，所以请将就一下。

### 文件系统权限问题

相信你已经注意到配置中的 `user: "${AIRFLOW_UID:-50000}:0"` 了，这里的AIRFLOW\_UID不是乱设置的，和你现在宿主机系统的当前用户（最好非Root）保持一致。 这样做的话，本机用非Root用户身份直接可以编辑Airflow里面的文件，其次，和git-sync的文件权限也保持一致还可以防止某个Container读写不了另一个Container的文件。

推荐运行`echo -e "AIRFLOW_UID=$(id -u)"`并且将输出复制到.env文件。

还需要获取DOCKER\_GID：`getent group docker | cut -d: -f3`，你会得到一个数字，把它设置为你的DOCKER\_GID去。不这么设置git-sync就会遇到权限不足的问题。

### git-sync的SSH Key问题

我们这里的方案是使用Git Repo管理所有的Airflow DAG，git sync的方式是给一个deploy key。无论GitHub还是我用的Gogs都支持这个功能。

生成密钥的方式如下：

```shell
mkdir -p git-secret
ssh-keygen -t ed25519 \
  -C "e.x. your email address" \
  -f git-secret/id_airflow_ed25519
```

随后把Public Key添加到Git中去。

生成known\_hosts文件防止连接的时候需要手动确认：

```shell
ssh-keyscan github.com | grep -v '^#' > git-secret/known_hosts
```

权限设置

```shell
sudo chown 50000:0 git-secret/id_airflow_ed25519
sudo chmod 600 git-secret/id_airflow_ed25519
chmod 644 git-secret/known_hosts
```

然后你就可以用下列命令验证是不是好用：

```shell
GIT_SSH_COMMAND="ssh -i git-secret/id_airflow_ed25519 -p 22 -o UserKnownHostsFile=git-secret/known_hosts" git ls-remote ssh://git@github.com/your-org/your-repo.git
```

我在实测的时候遇到一个问题，Deploy Key添加了但是命令不生效，这个时候用SSH Config的方式Clone Repo一次，再测就通了。

```
Host github.com
    HostName github.com
    User git
    PreferredAuthentications publickey
    Compression yes
    IdentityFile /your/private-key/path
```

而且一定要启用新的shell（有空我确认一下这里到底是怎么回事）。

### 在Airflow内使用Docker

由于我们使用LocalExecutor执行代码，所以负责连接Docker的是Scheduler Node。

我们只需要在Scheduler Node上挂载如下的Volume即可: `/var/run/docker.sock:/var/run/docker.sock`

之后我也考虑在内网使用TCP管理多个Docker节点，这样可以部署负载到多个机器上。

### 使用S3存储日志

你首先要准备一个JSON配置，必须是一行，可以用这个命令压缩：`cat your-file.json | jq -c .`

```json
{"conn_type":"aws","login":"your-access-key","password":"your-secret-key","extra":{"endpoint_url":"http://your-endpoint:your-port"}}
```

随后配置 `.env`：

```bash
AIRFLOW_LOG_CONN='{"conn_type":"aws","login":"your-access-key","password":"your-secret-key","extra":{"endpoint_url":"http://your-endpoint:your-port"}}'
```

最后，通过以下设置启用远程日志。

```yaml
AIRFLOW_CONN_AIRFLOW_LOG: ${AIRFLOW_LOG_CONN}
AIRFLOW__LOGGING__REMOTE_LOGGING: "true"
AIRFLOW__LOGGING__REMOTE_BASE_LOG_FOLDER: s3://airflow/logs
AIRFLOW__LOGGING__REMOTE_LOG_CONN_ID: "airflow_log"
```

### 踩过坑又忘了的FERNET

一定要设置AIRFLOW\_\_CORE\_\_FERNET\_KEY，这个Key会用来加密所有的敏感信息，比如Variable或者Connection之类的。

如果不设置，重启之后就无法读取之前设置的Variable或者Connection，而且API也会返回500。

实在忘记了就得把数据库的端口透出来，连上去以后删除connection和variable表里所有的数据才行。

生成一个随机FERNET Key的方式为：

```bash
docker compose exec airflow-scheduler python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
```

当然你也可以直接执行里面的Python代码。

### JWT 设置

JWT虽说不设置也行，但是如果每次都使用Airflow自己随机创建的JWT密钥的话，每次重启后之前的登录就会失效。

设置这两个环境变量即可：

* `AIRFLOW__API_AUTH__JWT_SECRET`
* `AIRFLOW__API_AUTH__JWT_ISSUER`

### 一定要添加DAG Processor

Airflow 3如果不设置DAG Processor的话就不会自动加载DAG的变化。

手动重新加载DAG的代码如下： `docker compose exec airflow-scheduler airflow dags reserialize`

## 技术选型和思考

### 为什么使用Local Executor

所谓[LocalExecutor](https://airflow.apache.org/docs/apache-airflow/3.2.2/core-concepts/executor/local.html)就是在Scheduler Node上直接执行。

其实多个Worker才是Airflow并发的杀器，但是考虑到我的任务并发不大，而且大多数时候是连接Docker去执行代码，本身的负载并不大，所以Local的完全够用，还能节约资源。

### 为什么选择S3存储日志

可以看到，我这里用的是另一个S3而没有在本地启动RustFS或者Minio。

其原因是我有一个NAS，NAS用来存储文件比我的电脑要牢靠一些（我设置了RAID以避免突发的硬盘损坏）。

NAS当然也可以运行Docker，但是硬件有局限，不适合运行一些太重的任务。所以最终的选择是NAS运行数据库（当然我因为别的原因，数据库没有放在NAS上），Git（我用的是Gogs）和S3服务器这种需要可靠存储数据的服务，而服务器（是一台旧电脑）的任务则尽可能的保持无状态。

### 为什么是PostgreSQL

简单来说，Airflow需要的数据库并不大（至少我的数据库如此）。理论上MySQL也不是不能胜任，但是两者的启动内存其实差不多，CPU也用不了多少。 在性能更优的情况下，PostgreSQL并没有占用更多的资源，那么我肯定选择PostgreSQL了。

虽然我没有真正地去横向比较过MySQL和PostgreSQL的性能，但是以我的使用经验来看，任何MySQL的服务如果遇到了数据、计算的问题，切换到PostgreSQL可以解决掉90%（暴论）。

其次，PostgreSQL提供真正的数据库，数据库里是带schema的，权限控制也更细致。这么一比MySQL就好像个残废了。当然，Airflow并没有用到PostgreSQL和MySQL有差异的这部分功能（毕竟还是要考虑兼容性）。

至于为什么不是SQLite，其实SQLite本身是非常牢靠的，但是仅限于单进程的场景。而Airflow至少要部署API Server，Scheduler等等服务，通过挂载文件夹共享的方式来共同使用总让人觉得不是很牢靠。如果Airflow能通过配置在一个容器中同时启动API Server，Scheduler等等，我可能会选择SQLite。

### 为什么不用K8S

这个问题有些可笑，问为什么不在家用K8S部署Airflow仿佛是在问自己为什么吃的苦不够多。

但是正好借着这个机会说一下家庭部署服务的事情，很多人的Home Lab上了K8S（或者K3S吧），然后配合ArgoCD自动部署（完全就是企业级的那一套）。其实大多数人用不到企业级需要的那些功能，反而因为K8S增加了负担。

K8S在设计上就比较适合跑无状态的服务——你有N个无状态的POD，需要规模化部署和管理，自动管理网络，那么K8S非常合适。但是很少有人的Home Lab要扛高负荷的。大部分的需求是相册、音乐、博客、笔记等等，抛开其中有状态的那个数据库不谈，基本没有几个QPS，所以K8S就是杀鸡用牛刀了。

相反，如果真的有高流量访问的需求，比起家庭网络，部署到云上是更好的选择。

当然了，是否上云可以参考这篇文章：[为什么我劝你不要使用云计算？](/other-tech/cloud-or-not)

## 本文的局限性

我虽然以前在K8S部署过Airflow 2，但是时隔多年已经忘了一部分，加上这是我第一次用Docker部署Airflow 3，所以很多配置都不一定是最优的，可能会有历史的痕迹。

其次，我放在airflow-common-env中的那些未必是全局都需要的变量，只是我太懒了，懒得去逐一分辨哪些是公共的，哪些是某些Container特有的。

而且，这个Airflow只有我一个人使用，所以很多配置从简了，比如没有配置OIDC Login，比如从GitHub或者Google登录。

如果你发现文章有任何让你不爽之处，欢迎给我提PR修改这篇文章。


# 开源CAN总线信号可编程台架

刚才开源了一款以前写的[CAN信号可编程台架](https://github.com/TsingJyujing/CAN-panel%E2%80%8B)

很早以前我接触了CAN总线和一些相关的测试技术，随后写了这个台架，但是一直没开源，原因主要是写的太烂，达不到我开源的标准。 后来我修修补补，虽然还是很烂，但是我开源的标准降低了啊，所以就开放了。 再不写点车辆相关的东西我都要忘记自己是车辆工程专业毕业的了。

具体的内容可以读Readme，我复制部分重点在这里。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-54083c8ed9c39013163a4e82633f9b1e4903a491%2F2020-05-05-22-19-05.png?alt=media)

## 简介

有很多设备可以读取CAN总线上的数据，或者模拟发生数据，而可编程的平台并不多，一般是借助厂商提供的API调用驱动，达成和设备进行数据交换的目的。

本软件的目的，是构建一个通用的CAN通信规范，可以兼容多种设备，同时有着统一的编程接口，可以对CAN总线进行各种数据操作，完成模拟或者测试的工作。我决定将我这部分工作开源出来，是为了所有饱受手工操作之苦的测试人员做一点事情。

成本起见，请尽可能使用便宜的设备，如果总线负载重，超过500帧每秒，请选择贵一点的设备。

## 不足之处

以下的不足之处，希望有志之士能帮我改进一下。

我最早写这些代码的时候经验不足，很多东西如果让我重新来过我一定会重新设计，比如

* CANSignal的结构设计的比较“硬”对于驱动的支持和测试任务的支持，都应该做成插件式，这样的话比较利于扩展。
* 关于选择脚本语言，JavaScript的资源会多一点，但是如果重新来过我可能会选择C#。
* 关于脚本语言的IDE，也不是很友善，如果重新来过，我可能做成插件式开发，打包之后可以加载到程序里。
* 再来一遍我可能全部使用WPF开发，主要是我之前开发VB6的经验让我更加熟悉WinForm的开发，所以仅仅是编辑器组件使用了WPF

最后关于源代码中的变量和函数的命名问题，我需要解释一下： 你可能会看见两种命名风格，一种是getDeviceId这样的Java式（lowerCamelCase），一种是GetDeviceId这种C#推荐的方式(UpperCamelCase/PascalCase)。 这个可能会让你维护的时候觉得有点肮脏。 但是由于我在开发的时候还需要写Java，Python，Matlab以及其它很多语言。在你同时写多种语言的时候切换风格真的是一件非常折磨人的事情，所以最早期的代码全部是Java式的命名（包括我早期的Python代码），最近我已经竭尽全力重构为C#式，但是工作量太大，一时难以全部完成。

还有一部分UNIX C式的命名往往是第三方的驱动引入的，这一部分我不准备修改，原因也很简单，开发驱动的人大多是写C或者C++的，他们写的C# API自然也是C的风格。


# 为什么我劝你不要使用云计算？

## 前言

这是一篇会引起争议的文章，我写下这行字的时候就觉得，如果这篇文章如果有幸被做云计算的大佬看到，一定会顺着网线过来爆锤我。

所以我今天不想说云这个技术的坏话，相反，我是比较期待云技术的，但是我认为云无论是技术上还是商业上都不够成熟，当然，我的见解也有可能是我和我服务的公司体量太小，大公司或许有什么套餐吧（我猜的，但是大公司一般都自己建云了）。

我今天写的文章，和我过去几年的经历稍微有点关系，也就是说，我的历史局限性让我写出了今天这篇心血来潮匪夷所思的文字，大佬轻拍。

## 正文

和解数学题一样，我来说一下简单情况：

当你的公司体量大到一定程度，就应该自己做云，享受成本价的云和云带来的便利。 事实上，国内一些有头有脸的大公司都是这么做的，其中一些还开放出来卖了，我们接下来说的云，就是这些出来卖的云，比如阿里云腾讯云百度云等等（当然也有专门做云，以云为产品的公司）。

对于剩下的公司，我的建议只有一条：**尽量不要上云**。

这里的尽量指的是，如果你创业非常初期，每年连在IDC放几个机架的钱都掏不出来，甚至居无定所，那么云是你的唯一选择。

下面就让我简述一下为什么这么做：

### 云的美

首先说说不得不上云的情况：如上所述的创业初期，其实也是不应该上云的，理论上说只要你有公网IP，是可以直接在家里放个UPS+机架的，这样比上云稳定性差点，但是便宜的多。但是国内大多数宽带是不给你公网IP的。所以你拿到的IP已经是多少层NAT以后了。

有一个特例，我以前在某西部城市办理宽带的时候，上面写着，我们不给你固定IP，如果你需要一个固定的IP，请（要）联（加）系（点）我（钱）们。哪是不给固定IP，压根就没有IP，安装好以后我打开路由器一看，老NAT了。

其实他们就是给我公网IP我也不敢当正经服务器用，因为我们小区楼下的交换机基本是这个状态：

![比这个好点儿](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d175da08ab206667df2d28fb302ad0c41749ca50%2F2020-07-19-17-41-24.png?alt=media)

也就比上面这个好点儿，移动联通电信公用一个交换机，从不上锁，师傅来安装的时候一路连插带拔，也不知道多少人闪断了网。

这个时候，云虽然贵，但是给你一种靠谱的感觉，你想象你的服务器会躺在干干净净，恒温18度，控制湿度的空调间里面，除了地震和挖断光纤也不太断网，专业的运维团队为你服务。

这个时候，云还有一些诸如函数计算这样的功能，你连服务器的钱都可以省了，真正的按照运行的量收费，也不操心宕机的问题。

事实上，偶尔会有实习生临时工不小心干掉你的数据，但是这个案例的几率不高，如果你没什么钱的话，没有人会为你写脚本的，所以看上去你可以高枕无忧。

### 云的坑

下面我说说云的坑：

21世纪什么最贵？人才？不不不，是云上的带宽。

想当年那个西部城市，50M的带宽售价326人民币一年，纵使这个价格乘以一百，也赶不上某里云带宽的价格。 当然你可能会说，上图那种盘丝洞级交换机带来的网络，可以和IDC的专业网络环境比较吗？当然不可以，但是差100多倍真的是超出我的认知了。哪怕我们把云的1bit当1byte来用，那都差了十几二十倍。

NTT的网络套餐，1G的光纤入户，2360日元一个月，有不固定**公网IP**（重启光猫的时候变化），就我的使用情况来看，和专业的IDC的网络，稳定性也不差多少了。 于是乎我一口气在家里搭建了好几个服务用，连Vultr几刀一个月的主机也不需要买了。 具体搭建了哪些服务可以参考我的blog：[互不联网时代的自给自足](/other-tech/net-self-sufficient)。

我对比了该云的其他产品的价格，比如数据库之类的，没法儿和Vultr这种专做廉价VPS的厂商比，但是价格和Google，AWS也还算是接轨的。唯独带宽，用金贵两个字形容不过分。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-66dd62c9a4d301b5936d04749b1c84364d250a05%2F2020-07-19-18-49-22.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-adaa819a2c614c206902c0d5328f9f6aa15361e9%2F2020-07-19-18-49-44.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-fd884eae90a67c5c41d11e0534eaeca2393ca766%2F2020-07-19-18-55-40.png?alt=media)

那我不用这个邪恶的某里云不可以吗？当然可以，于是我又去看了某讯云，基本是一样的价格。 某度云我就不看了，账号早在Pandownload的作者被抓之后，我就把用了十几年的老账号注销了。

对比一下，Google的云，带宽是基本不要钱的。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f22abf9778cbbb7ebb401bb58fa2fd74faca51b5%2F2020-07-19-18-14-13.png?alt=media)

我又看了Azure和AWS，发现都不太能注意到网络相关的设定，更加不会限制带宽。但是由于GFW的存在，国内的业务，尤其是稳定性有要求的业务，并不适合往上放。

我就不对比Vultr这种廉价机场了，和他们比价格太欺负人了。Vultr的总量每月1TB，带宽至少100Mbps的主机才5刀一个月啊，这个配置在国内其实可以应付大部分场景了。

\*\*为什么偏偏带宽这么贵？\*\*这里是我的个人理解，不一定正确：

因为在中国，大部分互联网生意本质上是 **流量生意**，我们常常说某头部大V，之所以说的头部评价指标是关注人数，转发量，浏览量点击量。大部分的互联网公司，本质上是广告公司，说来惭愧，我大学时代一直不能理解为啥互联网公司这么关注流量问题，在我幼小的心灵里，流量并不代表着什么，看的人多，不代表这个东西就好，这个是很朴素的道理，事实证明我的确Naive，它们就是广告公司，就看流量，看CTR/CVR，不然呢？看我的腿毛吗？ 要不然就是做O2O的服务，比如打车，或者外卖，然后骑手用户店铺三头收钱，配合一套大数据杀熟，榨干你的最后一滴。 当然也有可能这些都不是，因为也有可能是在骗投资人的钱。

这样看来，某云其实不是在收带宽费用，而是让你为你的业务增长交税，只不过收税的不是国家，是某云。 小带宽（1Mbps）起步，不至于让你的网站还没起来就因为钱夭折了，等你业务起来以后需要大带宽的时候，再猛烈收费，知道你业务起来以后不在乎这几万块钱——这种看人下菜的操作，我只能联想到个人所得税。

我为什么认定带宽价格不是移动联通电信的锅呢？毕竟国内也就这三家占了0.999壁江山啊！因为如果你自己有公司办公场所，开通有公网IP的网络并没有那么贵。

除了带宽的价格问题，国内上云的话，被DDOS的概率会比全部自己搞要高的多，可以看我的这个回答：[哪些创业公司把服务器放在腾讯云了？有哪些弊端？ - 清雨影的回答 - 知乎](https://www.zhihu.com/question/20983398/answer/316353756)

需要说明的是，我并不是说上云就会被DDOS，或者云计算厂商监守自盗，只是我观测到了上云和不上云两个场景下，被DDOS的概率分布有着明显差异，也不知道近几年改善了没有。

## 总结

看上去我花了很多的篇幅在黑云计算，但是我并不是对云计算这个技术有什么偏见，我觉得王坚博士的《在线》写的很对（虽然此书有打广告的嫌疑），云计算，会成为水电煤一样的基础设施。

但是我们不想为带宽和高防付出太多的成本，但是水污染严重的地方，净水就显得弥足珍贵，雾霾肆虐的地方，空气净化器就十分必要。同样，国内的网络环境让我们不得不承受高昂的带宽价格。

我有一个简单的判断方法，如果某一天，我们可以用不高的成本在云上自己搭建一个网盘（用函数计算连接其它网盘的不算），那么我觉得云计算就慢慢变得成熟了。

真心的希望云有一天能真正成为和水电煤一样的基础设施。

写于 2020年7月19日


# 干掉吸血雷，重塑和谐P2P环境

在我不懂技术的时候，经常使用迅雷下载各种电影，软件，迅雷很好的解决了死链之类的问题，现在来看，在那个带宽不充足的年代，缓解了一部分服务器的下载压力。 但是迅雷对于整个P2P环境的破坏是极大的，你经常可以在peer里面看到很多客户端名字为XL00+一堆乱码，或者Xunlei，7.x.xx之类的，永远只从你这里下载，而不给你上传。

说实话，我不是个吝啬带宽的人，我自己用的是光纤，而下载速度上传最快都是可以突破百兆的，我还有公网IP。所有的下载资源，我都会等待分享比率为2.0或者等了很久没人来下载我才会撤。 P2P要能玩下去，平均每个人的分享比率需要大于1.0才可以。

而迅雷，则是这样做的：

1. 如果对方不是迅雷的客户端，那我就只下载，而不给他上传数据。
2. 如果对方是迅雷的客户端，则优先给会员数据。

除此之外还有百度的离线下载，Xfplayer等等等等。

最后导致大家开发了反吸血的qBittorrent。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5f3ed27909bc1851d423331f66db2f2276309ea3%2F2020-05-05-18-59-17.png?alt=media)

## qBittorrent 反吸血增强版

如果你是Win或者Mac的客户端，可以尝试下载这位大神Fork的反吸血增强版：

<https://github.com/c0re100/qBittorrent-Enhanced-Edition>

Mac用户可以直接用Brew安装：

```bash
brew cask install c0re100-qbittorrent
```

Linux的用户可以自己编译，下载软件源的编译好的结果，或者使用Docker版：

<https://hub.docker.com/r/superng6/qbittorrentee>

## 关于反吸血的一点畅想

P2P的协议的缺陷在于，假定每个人，尤其是软件制作者都是善良的，这个假设非常脆弱。

我猜想以后迅雷这样的软件可能会冒充其它软件来获取数据，对于这种方式，我们需要其它的屏蔽手段：

比如，我们可以每个人在下载的时候尝试探测对方。

1. 你还没数据，对方已经下载了85%，但是拒绝给你提供任何数据。
2. 或者你已经给对方提供了某个block的数据，但是和对方请求这个block的数据的时候却无法下载

这样的例子多次出现，可以BAN了对方。

对方有可能在给别人上传而顾不到你，这个时候，不能运用这些准则百分百判断对方就一定是吸血，但是我们可以设定行为权重，如果出现次数较多，那我建议自动BAN掉这个IP一周再释放。 而P2P协议应该也允许你获取其他人的黑名单，而你可以设置参考权重，选择参考或者不参考。

甚至可以用最优化的理论来调节这些权重，在不影响下载和分享的前提下，尽可能屏蔽掉多的IP，这样不好好分享的人就很难捞到好处，而又不至于对P2P生态破坏太大。

有些人可能说，我上行带宽吃紧（比如电信，下行和上行差距就非常大），那照我说，上行带宽吃紧就不要参与这个P2P了，连100k都贡献不出来就算了。


# 福冈外免切替（中国驾照换日本驾照）攻略

## 简介

福岡県外国免許からの切り替え攻略。记录一下福冈市外免切替的流程，以及相应的经验、资源。其它的县应该也是类似的流程，不过我没有经历过。

我是2021年8月～9月办理的手续和考试，金额和流程仅供参考。

由蒙娜丽派于2025年更新。

## 流程

目前只支持在筑豊試験場进行考试，当然情况可能有变，建议先找离你最近的試験場问一下。

最详细的内容可以看这个最官方的文件：[外国免許からの切り替え手続き](https://www.police.pref.fukuoka.jp/kotsu/unshi/013.html)。

### 材料准备和递交材料审核

需要准备以下材料：

* 护照
* 在留卡
* 记载了国籍，且没有My Number的住民票一张
  * 可以去区役所办理
  * （没测试过）也可以用My Number Card去便利店打印
* 有效的外国（这里是中国）驾照
  * 中国驾照的透明壳有一面有星星，有一面没有，制证时装反了会被认为是假证，提前确认一下有没有装反。
* 驾照翻译
  * 可以找领事馆或者[JAF](https://jaf.or.jp/common/visitor-procedures/switch-to-japanese-license)翻译。
  * 因为疫情，我找的是JAF邮寄材料翻译
* 证件照
  * LINE搜索ピクチャン，可以在线编辑证件照后到便利店打印，比较便宜，但是考拍照技术，容易拍丑，哈哈哈哈。
* 能证明你在拿到驾照后在该国家住了三个月的材料
  * 护照出入境记录或者支付宝出入境电子记录
* 申请书（到了試験場会有提供）

出入境记录最好打电话给試験場询问一下。我的护照办理到初次入境是有3个月的，但是居然不能作为证据，我最后带上了我的以前工作的劳动合同和毕业证才完成证明。如果不打电话，可能审查会白跑一趟。

带着这些材料，于工作日的下午1点到达筑豊試験場，走到最深处去交材料，交完材料会预约下次材料审查的面试时间。当天不能进行面试审查。

面试的内容是很细致的，包括你在国内考试去没去驾校学习，花了多少钱，学的啥，上课吗，上啥课，考几个科目，每个科目都考啥，你考几分，最后怎么领驾照，考试用车是考场的吗的等等。

忘记一些也没关系，忘记了就说忘记了，由于多年前考的，我忘了好多细节，面试也没问题。就当和警察唠磕，过程还是比较放松愉快的。

听不懂日语的可以带翻译一起去，但是面接审查处有实时翻译投屏的设备，透明的屏幕在你和审查官中间，非常方便，但是准确率未知。

面试和审查材料通过后，就会给你一张线路图（就是考试线路，有且仅有一条，2024年换了新路线，沿用至今）。这个时候可以预约第一次考试的时间，看一下自己的日程表，是可以选考试的日子的，但是有要求的话可能会让考试拖晚一点。

面试结束就可以回去了，在约定的日子进行笔试和初次的技能考试。

### 笔试（Paper Test）和初次技能考试

到了约定的日子（可以电话更改预约），上午8：00-8：30受付，过时不候。先到2号窗口要买一个印纸（其实就是缴费凭证，印纸可以提前买，如果你乐意，上次面试结束以后就可以买，买完以后要写好地址和拼音的姓名），费用的话上一次面试的时候警察会说，或者你和窗口说你要考什么，窗口的工作人员也知道。我考的时候是2550日元。

同时需要填写一张质问票（也是考场提供），就是问你有无什么不适合开车的大病。（真有病就不来考试了，所以都写无然后）署名（拼音）日期。注意，质问票的日期是不允许涂划更改的，写错了需要重新填写。

填好了去窗口排队，等叫号，然后就会有人带着去测视力和做题。题目一共10道，答对7道合格。可以选择中文题目，下面会附有日语。

这个笔试不需要准备，只需要常识就可以做，大家都是在国内考过科目1和科目4的人，肯定没问题，无论国籍还是性别啥的，我还没有遇到任何笔试不过的人。

笔试结束以后，就到技能测试等候室等着，考官会给初次考试的人讲解技能考试流程，但是并不是所有的考官都会说细节，而且说明只有日语。

说明完成后，就等喇叭喊人去考试，考试一般是2个人一组上车，后面的人可以看着前面的人考，据说是为了防止串通，一个考完以后叫下一个坐车后面。

### 技能考试失败以后的重新测试

如果你不幸失败了，考官会给你说明你失败的原因，但是不一定会把你所有的问题都说出来。你可以和考官多唠一会儿磕，考官就会和你多说一点。

之后考官会把你的照片撕下来还给你，这是你下次填写申请书的时候要用的，不要急着回家，需要去5号窗口按门铃重新预约考试时间，即使日程未定容易变化也不要紧，先预约一个早一点的，之后可以电话变更的。

再次考试的流程和初次考试不同，是下午1点交材料，但是你需要重新填写一张申请书（第一次的），质问票，买一张新的印纸。推荐在考试失败之后就提前购买印纸，填写质问票和申请书等等准备材料，这样下次可以省事儿一点。

记得全部填写好以后再交给窗口，不要到了窗口再填写，有些警官没有耐心，会让你去一边填写然后重新排队。 交完材料就会测视力，测完就到技能测试等候室等着喇叭喊人去考试。

如果再次失败就重复这个流程，直到成功，或者距离你初次考试/材料审查六个月，审查的材料失效为止，这个时候你需要回到最初重新笔试/提供材料。

### 成功以后拿证的流程

如果你成功了，考官会恭喜你，并且让你去买印纸，到了窗口，你说试验合格的印纸，窗口就懂，我这次是2050日元。

印纸还是要写好地址和名字，坐在交材料时的窗口附近耐心等就行，所有的考试结束以后，才会统一组织拍照和办证，女生建议提前化妆。

### 关于驾校

练习还是有必要去的，日本的规则和中国有不同，有许多需要注意的细节。尤其是坐到右侧开车以后，车体感觉也会有不同，日本的S弯和直角弯比国内的似乎窄一些，这都需要适应。即使在国内是个老司机，也建议找个驾校练习一下，而且驾校的练习一般都在周末，不会牺牲工作日，考虑到可以减少失败次数，能省事不少。

推荐几个驾校和教练：

1. 野間自動車学校：Google搜一下就可以搜到，也可以用LINE搜索，直接可以在LINE上预约课程和咨询，外免切替的课程费用是1万5千多日元左右2小时，训练场地就是考试场地。教练只说日语，好处是能提供和考试一样的车子，教练很严，是按照120分的高标准去训练的，这对考试有好处。
2. 一位日本教练，但是能说中文，需要微信号请给我发邮件。教练很认真和耐心，关于车体感觉有一套教学方法。不过训练车和考试车车型不同。
   * 微信号（经过教练允许发布在此）：`kazunori1020`
3. 理论上，只要你有朋友（3年以上驾龄）有车，带好印纸、誓约书、驾照、你的在留卡或者其它身份证件，去筑豊試験場申请预约，费用大约2750元1个小时，周末开放两天，一天开放5个小时。找驾校的话这部分费用是包含在学费里的。但是预约需要一天，去练车需要另外一天，对个人尤其是住在九大周边还是很麻烦的。
   * 考场场地需要预约抽选，目前约了基本都能约的上（2025.3）。

关于一些听闻的坑：

* 有些教练会带学生“打野战”，也就是找乡下没人的地方去练车。这个万不可取，一旦被抓住，就是无证驾驶，罚款不说，还会留下案底，签证会有麻烦，且5年内不得考驾照。练车一定要去专用的练习场或者周末可以租筑豊試験場这种真考场练习。

### 关于考试细节

外面切替的考试只有一条线路，但是通过率不高，因为细节太多了。下面说一些：

* 确认动作幅度要大，否则考官不觉得你确认了，可能扣分
* 十字路口前后的换道时，速度要在30～35km/h左右，速度太低也会扣分
* 在视线被遮挡的区域要探头左右反复确认，且越慢越好，确认安全以后就要加速通过
* 过十字路口转弯的时候速度不能太快，既不要压到中间的菱形，也不能离得太远。
* 过障碍物的时候离障碍物太远也会扣分，骑在中线上绕过去就可以
* S弯或者直角弯掉下去以后不要慌，可以倒车再来，最多可以搞3次
* 除非是自然弯之后就要继续左右转弯，否则不要打转向灯，会扣分
* 除了转弯这种需要低速的场合，其余场合不能太慢。一般在20km/h上下，否则也会扣分，一些直线路虽然短马上要转弯但是也要加速再减速，按照某位兄弟的说法：“你得踩一脚油门让考官知道这车是有发动机的”。
* 50km/h的地方尽量不要超过50，看准码表，刚到50就松开油门比较好
* 中途走错路是没问题的，考官会引导你回去，但是回到正常线路的过程中的失误（比如忘记确认等等）也是会扣分的
* 直角弯右转要贴左，左转要贴右，否则内轮差会让后轮掉下去
* S弯让前轮贴着外侧曲线过，否则内轮差会让后轮掉下去
* 左转的时候要贴左侧，这个要靠车体感觉
* 没有红绿灯等的路口一定要减速左右确认再通过
* 左转右转（或者贴左贴右）的流程是：打转向灯，看中镜，看左（右）侧后视镜，转身转头亲眼看左侧或者右侧（这个中国国内是不要求的，其实实际驾驶也不建议这么做，比较危险），然后才动方向盘。
* 下雨天一定要开雨刮，但是有些人喜欢用雨刮的点来做车体感觉，除非大雨，雨刮杆往下压一下开慢速就行。建议不要用这里做车体感觉。

#### 考试路线

（2025年3月）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a6f98afdd618bac7db95a9ea5b4440226a39af31%2Froute-1.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4a2f0de53b795966f95b0bafa9f27d63589be22c%2Froute-2.png?alt=media)

### 一些tips

* 上半身短的人，尤其是女生，记得调高座椅高度，椅子右侧有个很大的齿轮，一直转它转到底，座椅调高有利于直角弯和S弯的视野。
* 记得穿底薄薄的鞋，越薄越好，有利于感受踏板。考场车的踏板过于灵敏，轻轻一踩车就猛向前冲，要轻踩踏板。
* 观察时不能仅转头，要真的看向后视镜和后方，考官席位有目線鏡，可以看到你是不是真的看了。
* S弯或者直角弯掉轮了不要紧，要先看后视镜确认安全之后倒车重来，如果没有别的扣分，可以倒车三次。不可直接开走，也不能不看后视镜。

## 心态

考好几次不过的话心态容易崩溃、至少也是焦虑。也有人考10次不过崩溃后，大哭把考官骂了一顿然后再也不考的。 的确有特别严格到变态的考官，喜欢在一些没人注意的细节故意挂人。每个考官的标准也是不一样的，这个考官眼里没问题，下一个考官眼里就是失误的可能性也是有的。

但是考虑到每次的考官可能都是不同的，所以概率上你几乎总会遇到一个比较和蔼温柔的考官，所以只要尽力发挥，等到遇到正常一些的考官就能过。

考试和真正开车是不同的东西，考不好不代表开车技术差，考过了也不意味着上路开车就没问题，更何况考试有一点运气的成分（主要是考官），所以不必崩溃。

## 蒙娜丽派的考试没过理由

* 离一時停止线过近
* 转弯时车没有完全回正就加速，入弯减速不够
* S弯掉轮没看后视镜
* 行驶过程中三点确认时，车会跑偏

## 蒙娜丽派听说的考试没过理由

* 整体开的太慢了
* 过障碍物时开的太快了/离障碍物太近了
* 贴边不在30cm范围内

## 资源和参考资料

* [(筑豊試験場)外国驾照换日本驾照考试视频](https://youtu.be/rlFL0531isk)：如果在筑豊試験場考试的强烈推荐多次观看这个视频，但是这个视频没有解说，所以最好是在去筑豊試験場练习过以后，心中对各个确认点有数以后再看，否则看不出门道。
* [中国架照换日本架照视频教程 外免試験場路線](https://youtu.be/jUfqk7VPcbI)
* [【外免切替】如何把中国驾照换成日本驾照？](https://zhuanlan.zhihu.com/p/21901336)
* Youtube上用日语搜索可以搜索到很多针对单项目的攻略，比如S弯的：[【教習所】S字カーブの走行方法](https://youtu.be/l3M0k36PYzE)，这里省略。
* [我在日本的交通违章故事 | 如何把国内的驾照换成日本的【冷水vlog】](https://www.bilibili.com/video/BV1Gv4y1Z7Ub)
* [在日本如何用中国驾照换日本驾照？外免切替保姆级从0开始准备资料到一次过及格的全过程分享](https://www.bilibili.com/video/BV1SK411u7m6)


# 构建家用NAS过程中的碎碎念

## 前言

最近存储告急，要给自己的数据找个家，所以开始琢磨NAS的事情，购买，搭建NAS的时候，阅读了很多资料，也发现了不少坑，留在这里，给大家参考。 本文可以看作一个组建自宅或者小型企业使用NAS的入门手册，很多措施或因地制宜、或需要有效利用已有的电子产品，稳定性也并非最优，因此并不适用较大规模的商业场景。

## NAS是？

网络附加存储（英语：Network Attached Storage，缩写：NAS），粗略地说，就是可以网络访问的一个硬盘。如果你没有概念，可以先阅读一下Wiki：[NAS 维基百科](https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E9%99%84%E5%8A%A0%E5%84%B2%E5%AD%98)，如果你没法翻墙，可以阅读：[NAS 百度百科](https://baike.baidu.com/item/NAS/3465615)。

在我来看，NAS就是一台电脑，只是这台电脑装着比别的电脑多得多的硬盘，并且利用厂商开发的特殊软件完成一些特殊功能。对于一个工程师来说，用旧电脑（台式机，可容纳多块硬盘才行）DIY一个NAS可能更加便宜和可玩，我手头没有这样的旧电脑，也忙于其它事情不愿花几周去折腾（其实折腾过了），所以就买了一台NAS。

## 你是不是需要NAS？

### 从数据量和性质上考虑

那么你是否需要NAS呢？如果：

1. 你有大量（我的理解是超过300G）的静态数据（也就是写入后只是读取不需要修改的数据）需要可靠存储，可以选用NAS。
2. 如果你有大量（我的理解是超过10G）的动态数据（也就是随时需要修改的数据）需要可靠存储，可以选用NAS。
3. 不要求可靠存储，但是数据量大于8TB的。

下面是我写的一张家用数据备份的选型表，仅代表个人意见。而且各家的数据不一样，往往是混合起来使用。

| 是否需要可靠存储 | 数据类型  | 数据量    | 应该选择               |
| -------- | ----- | ------ | ------------------ |
| 否        | 动态/静态 | <8TB   | 电脑硬盘或者移动硬盘         |
| 否        | 动态/静态 | >8TB   | 可以考虑NAS（RAID0）     |
| 是        | 动态/静态 | <10GB  | Google Cloud 之类的网盘 |
| 是        | 静态    | <300GB | 移动硬盘或者蓝光碟          |
| 是        | 动态    | >10GB  | 可以考虑NAS（不能用RAID0）  |
| 是        | 静态    | >8TB   | 可以考虑NAS（不能用RAID0）  |

如果你不需要可靠存储，或者数据量不够，NAS则未必必要。对于需要可靠存储的情况：如果你的静态数据少于300GB，那么使用移动硬盘+刻录蓝光碟备份是比较好的选择，如果你的动态数据少于10G，那么可以选用GoogleDrive或者OneDrive这样的网盘来的更加轻松，也可以在主机内用两个硬盘分区组建RAID存储。 再或者，你根本不需要可靠存储，数据丢了问题也不大，那么买一个大一点的移动硬盘或者电脑内置硬盘是比较好的选择。

### 从可访问性上考虑

举个典型的例子：如果你是个小工作室，大家分散办公，且需要剪辑视频公用一些素材，那么可以选用NAS。但是NAS不是必要的，一个略有经验的工程师同样可以用电脑搭建起来同样的东西，如果你对网络没什么概念，那么NAS可能是最好的选择了。

## NAS的购买

我们这里不考虑买到假货的问题（一般不会，因为厂商有专属软件），着重说一下NAS购买时需要注意的问题。 我也会说一下我自己的选择：QNAP TS-451D2（4GB）+自己又买了一块4GB内存 + 西部数据NAS红盘（8TB），但是我的选择未必适合你，请耐心读完自己选择。

### NAS本身

在购买NAS本身的时候，首先要看硬件，最重要的是以下几点：

* 可容纳硬盘的数量
* 网卡的速度
* CPU的指令集

可容纳硬盘的数量决定了你的存储能力的上限，也决定了你能做怎样的RAID（下文慢慢说），以及是否有空余的槽位用SSD做访问加速。如果你要可靠存储的话，槽位千万不能小于2个，否则无法组建RAID1。 我自己购买的是4个槽位（SLOT）的NAS。 网卡的速度决定了你的访问速度，一般来说，千兆网卡已经是标配了，一般不需要关心，对于有线布线连接困难的场景，你可能需要选用带无线功能的NAS，或者确认一下是否可以用外接无线网卡解决。 CPU的指令集一般是不太被关心的内容，但是如果你需要在NAS上运行一下自己的程序，或者NAS支持容器化，那最好选择AMD64指令集的NAS。

厂商的话，一般也就是群晖，QNAP，IO-DATA之类。搜索NAS大多可以搜到这些厂商，这里就不做推荐了。我自己的话，最后选了QNAP。

既然是电脑，当然也可以改造台式机，甚至自己购买刀片服务器，但是我在网上很难找到某种台式机究竟支持多少硬盘的槽位的数据，所以干脆买了专用NAS。

### 硬盘（HDD）

机械硬盘便宜，存储空间大，是NAS的首选。但是NAS的话，最好选用NAS的专用磁盘，比如希捷的铁狼或者WD的红盘之类，NAS盘和其它盘的不同之处在于磁盘的控制器会主动向系统报告磁盘错误，而不是卡住不动。长时间卡住不动的硬盘会被NAS认为是坏盘而踢下线报错。考虑到硬盘其实没那么容易坏（~~其实一摔就坏~~），所以你用非专门为NAS设计的硬盘也不是不可接受。

容量你自己肯定会考虑，除了是否专用NAS的控制器以外，最重要的一个概念是SMR与CMR。简单来说，成本大致一样的情况下，SMR通过牺牲了读写速度换取更大的容量。

也可以说，同样容量下，SMR成本低速度慢。如果是组RAID1的话，对随机读写性能要求更高，SMR就更慢了。（事实上，SMR不仅速度慢，而且不靠谱，数据相当容易丢）。 比起希捷的故障率，WD（西部数据）的NAS红盘系列一直是“靠谱”的象征，直到有一天，有人发现WD的部分容量的NAS红盘居然用SMR技术，并且因此造成自己数据损坏。 于是乎被群众吊起来暴打，被希捷嘲讽，打到后来西数慌了，网上公布了所有SMR型号的硬盘（但是不全面，比如绿盘就没人说得清楚，但是估计有SMR）。 用NAS的人一定对数据也是比较重视的，大多会也组RAID1，所以WD的NAS系列红盘用SMR真的是杀人诛心啊。

企业知不知道自己的盘垃圾呢？当然知道，CMR的盘一般能保3年，SMR的盘一般只保1年。作为消费者怎么识别SMR呢？很简单，我只看购买页面+官网查询型号，没有说是CMR的一律认定为SMR，宁可错杀不要放过。

那么SMR就真的一无是处吗？也不是，SMR比CMR环保，且SMR比CMR要便宜，从成本的角度出发，可以买一块放不重要的毛片儿啊电影啊之类的数据。如果不是经常读写的静态数据，也可以用来组RAID1保证可靠性，也可以降低成本。但是如果成本不是那么敏感的话，我还是不建议在NAS中使用任何SMR盘的。

虽然希捷很良心的不在NAS盘里用SMR，但是故障率仍然比西部数据的高，所以我最后还是选择了西部数据的NAS红盘，并在购买前确认该型号是CMR。

#### 对IO-DATA的批判

这里需要批判一下IO-DATA，我本来看上了一款IO-DATA的家用NAS，除了硬盘的拆换不是很方便，以及用的CPU是ARM架构而不是AMD64以外没什么问题。 问题就出在内置的硬盘上，内置的硬盘有两个问题：价格相当黑心，以及用盘相当不专业。

IO-DATA这款机器在Amazon上裸机的价格是不到25000日元左右，16TB（2x8TB）版本的价格是112800日元，换句话说，折合每块8TB盘的价格是43900日元。 如果用这个价格去买8TB的NAS红盘（CMR），价格是20000日元左右，4W+日元的价格我完全可以买最贵的金盘系列了。

但是IO-DATA内置的到底是什么盘呢？很难找的一个宣传画册上的官方照片显示是绿盘，我之前在实体店探查过是似乎某种SMR盘。这就是让用户花最贵的钱，买最差的盘。 我去实体店看过包装，包装上的小字写了盘的型号（我应该没记错），更鸡贼的是IO-DATA的官网页面，官网上不放包装的高清大图。 因为高清大图可以看清小字，为了不让你看清楚小字，所以官网一律是640\*480的图。

更更鸡贼的是：如果你去IO-DATA的替换配件售卖区，那里卖的盘基本都是CMR的好盘，比如WD的红盘，只有内置在机器中的盘是绿盘。

### 硬盘（SSD）

即使最便宜的SSD，存储成本也是HDD的4倍左右，所以一般是不选用的。

不过SSD贵在速度快，所以有的NAS（比如QNAP的Qtier技术）会支持SSD和HDD共同组成存储池，数据尽量存在SSD里面，随后将用不到的数据挪到HDD里面，这样可以在日常使用里大致获得SSD的性能和HDD的成本。类似Apple的Fusion硬盘技术。

说到SSD的选购，其实是个很深的大坑，影响一个SSD最终性能的是主控，缓存和颗粒共同决定的：

1. 主控的话花样比较少，只是有好坏快慢之分，垃圾回收算法先进落后之别。
2. 缓存的话有两层意思
   * 一层是用内存（DRAM）颗粒加速访问，这里花样也比较少，
   * 另一层是颗粒“降级”，比如明明是QLC或者TLC，但是当成SLC用，颗粒容量暂时减到几分之一但是速度就能大幅提升，等到写入数据超过这部分再启用TLC或者QLC，这个时候写入就慢了。所以在测试的时候，可能会出现开始写入速度很快，但是写入了几分之一的容量后开始掉速的情况。
3. 颗粒的花样就多了：
   * SLC/MLC/TLC/QLC，指的是一个单元（Cell）存储1/2/3/4Bit的信息，也就是把电压分为2/4/8/16份。随着信息量增大，可读写次数（寿命）下降，速度也相应的变慢。这里多说一句，虽然寿命下降，但是正常使用的话一般能比电脑都活的久……
   * 原片/白片/黑片：你可以理解为可靠性逐层递减，白片的意思是，原厂最严格的测试不合格，但是能用，所以不打标签出售给二线小厂用。黑片就是残次品，一般会在颗粒上划线表明报废，一些黑心厂商会用，比如金泰克七彩虹之类。
   * 批次：颗粒是需要调校的，准确地说调整的不是颗粒是主控。如果不是同一个批次，调校就无从谈起，就会有性能和稳定性的问题。

这里列出一些SSD厂商的黑料：

厂商宣传自己的SSD的时候很多都宣传“最高读写速度可达”，这里的读写速度往往是降级读写速度，一旦写入达到一定容量，速度就立刻掉下来了。 还有一些厂商会针对测试软件做优化，监测到写入测试数据的时候，实际不写入硬盘，导致看上去性能爆炸。 以及这个参考材料：[这些硬盘不要买！2020那些不推荐的固态硬盘](https://zhuanlan.zhihu.com/p/166162142) [「存档链接」](https://archive.vn/hSDBR)

* 大唐 SSD 虚假宣传，将使用QLC颗粒的固态宣传成TLC
* 长芯 SSD 虚假宣传，将使用QLC颗粒的固态宣传成TLC，还把低端入门主控宣传为高端主控
* 海康威视 SSD 主控降级虚假宣传，颗粒黑片回收片混用，QLC冒充TLC，曾被认为是国产固态之光，后来发现和上面都是一路货色。
* ……

总而言之，如果你需要对常用文件进行访问加速，那么可以选一块不大的（比如100G左右）的，性能较好的SSD，组成Qtier存储池。

对于使用电脑DIY NAS的朋友，我推荐这个项目：[Open Cache Acceleration Software](https://open-cas.github.io/) 相信会有所帮助。

## NAS的搭建和管理

### 关于RAID

如果你不知道RAID是什么，可以先阅读[维基百科 RAID](https://zh.wikipedia.org/zh-cn/RAID)。 简而言之，你需要可靠存储的时候，可以组建RAID1，这里我不是很推荐RAID5或者RAID6，随机读写的性能并不好（MongoDB就推荐用户不要用RAID5/6，参考[这个文档](https://docs.mongodb.com/manual/administration/production-notes/index.html#raid)），更重要的是，一旦硬盘失效，重建的成功率太低了。

我的话组建了一个2个8TB磁盘的RAID1，这样就有了8TB可靠的存储空间。之所以可靠存储要靠RAID，还得从机械硬盘的失效说起。 硬盘的失效，往往难以预测，大多数的厂商都说自己有算法可以根据S.M.A.R.T.参数预测失效，但是实际上，预测并不容易。（如果你想详细了解S.M.A.R.T.，可以看[这个Wiki](https://zh.wikipedia.org/zh-hans/S.M.A.R.T.)）。

曾有云存储的厂商做过统计，这里是一篇2014年的Blog，在4万块硬盘存储100PB的数据上的研究表明，SMART的5，187，188，197，198参数对失效预测有用：[Hard Drive SMART Stats](https://www.backblaze.com/blog/hard-drive-smart-stats/)

既然我们难以预测失效，那么我们不如随时备份数据，这就是RAID的作用。 需要说明的是，SSD没有HDD的这个问题，只是单纯的贵而已……所以SSD组RAID的需求就不那么迫切。

如果你是用自己的主机搭建RAID，除了买专用硬件以外，也可以试试软件RAID，详细请参照这个Blog：[Linux下利用mdadm设置软件 RAID](https://github.com/TsingJyujing/blogs/blob/master/other-tech/mdadm.md)

### 网络文件协议

一般的网络文件协议有SMB/NFS/FTP/AFP（苹果才用）/...。如果你只是简单的存取普通文件，那么SMB就可以了，如果你在Linux上，希望某些应用程序的数据放到NAS上，那你最好用NFS的方式挂载，SMB（或者说CIFS）不支持诸如软链接之类的东西。

但是SMB的好处是简单易用，带较好的权限控制，NFSv4协议的话按说是有ACL的，但是很多厂商没有实现（可能是出于兼容考虑），挂载也是使用IP地址在做验证，所以安全性不如SMB协议。

Linux上的话，NFS我就不介绍了，SMB协议可以用Samba软件实现。

### 特殊存储协议

广义地说，兼容Amazon S3接口的存储程序，比如Minio或者SeaweedFS，乃至MySQL或者MongoDB这种数据库也算是网络存储。

比如我希望存储一些非结构化的数据，所用了容器启动MongoDB。 这个主要是针对程序员群体的，可能会需要Amazon S3兼容存储。

其实有好几种方式提供S3兼容的存储，我买的NAS可以安装厂商开发的一个叫QuObject的程序，可以让文件系统兼容S3或者Swift的接口。也可以用容器技术启动Minio或者SeaweedFS（当然还有很多其它开源的S3存储选择）。 但是我最后选的是SeaweedFS，因为我的小文件太多了，其它两种方式固然方便——指的是直接访问文件系统的时候可以直接看到目录结构，但是小文件的存储无论对与NAS还是普通电脑的文件系统都是一个灾难。 不过有一点好的是，其实QNAP已经提供了Volume这个东西，所以就算很多小文件，备份起来并不是那么痛苦。 不过我的SeaweedFS在这里真的是牛刀杀鸡，SeaweedFS是可以支撑集群乃至多地存储的系统。

如果你需要这些协议或者功能，这里我推荐能够运行Docker Container的NAS，如果你主要依赖S3协议或者数据库，我更推荐你自己用电脑搭建，因为这个时候，你的主要访问需求并不是文件了，而主要的负载也不再是IO而很有可能还有大量计算，NAS的CPU一般都比较孱弱，往往特殊设计的硬件加速，所以这个时候自建可能更好。

### 网络

这一节主要给需要远程访问数据的人。

在之前的文章：[为什么我劝你不要使用云计算？](/other-tech/cloud-or-not) 里面，我详细介绍过如果家里有公网IP，可以完全不用云服务自己搭建，或者干脆使用内网穿透，这里也是同理。就算你自己没有公网IP，厂商一般也会提供远程访问的方案，但是这样相当于数据要从厂商的网络上经过，有泄漏的风险，所以厂商一般也会提醒不要用这种方式传输机密数据。

同时需要注意的风险是，比如为了配合监管，[群晖 NAS 用户须在年底前绑定手机号，否则将 “断网”](https://www.ithome.com/0/514/611.htm)。所以之后如果为了配合监管，对经过厂商网络传输的数据进行审查也不是没有可能，请务必注意（不过目前看不太可能把审查下沉到个人机器里，我大胆的推测，并不是审查者不想做这样的事情，而是不论数据量还是计算量都不可行）。

除了这种方式，还可以用[ZeroTier](https://www.zerotier.com/)来组建跨区域的局域网，我自己目前用的也是这种方式，当然我同时也有公网IP，但是局域网的方式要更加安全。

## 总结

本文主要介绍NAS以及其附件选购的时候要注意的点，以及一些网络服务的搭建。看完文章你应该明白什么时候需要NAS，需要什么样的NAS，需要什么样的硬盘。

如果有错漏之处，或者新的内容，欢迎联系我。

## 附录

### 一些 Docker Compose 配置

#### MongoDB

```yaml
version: "3"
services:
  db:
    image: "mongo:4"
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: <your own username>
      MONGO_INITDB_ROOT_PASSWORD: <your own password>
    command: "--wiredTigerCacheSizeGB 1.0 --auth" # Modify the memory by your hardware condition
    ports:
      - 27017:27017
    volumes:
      # You should change your own path or use docker volume here
      - /share/Container/data/mongo/db:/data/db
      - /share/Container/data/mongo/configdb:/data/configdb
```

#### SeaweedFS

```yaml
version: "3"
services:
  app:
    image: "chrislusf/seaweedfs"
    command:
      - server
      - -dir=/data/data # Should create dir manually before start
      - -s3
      - -s3.config=/data/s3.json # Define user and authorization in this file
      - -s3.port=8333
      - -filer
      - -filer.dirListLimit=50000
      - -master.volumeSizeLimitMB=4000 # ~4GB per file
      - -volume.max=1000 # 1000 * ~4GB = ~4TB in max
      - -volume.index=leveldb # You can remove this line if you'd like to put index data in memory
    ports:
      - 8333:8333
    volumes:
      # You should change your own path
      - /share/Container/data/weedfs/:/data
```

#### Node Exporter

```yaml
version: '3'
services:
  node_exporter:
    image: quay.io/prometheus/node-exporter
    restart: always
    pid: "host"
    command: ["--path.rootfs=/host"]
    volumes:
      - "/:/host:ro,rslave"
    ports:
      - "9100:9100"
```


# 互不联网时代的自给自足

## 前言

本文主要讨论如何自行架设各种网络服务，涉及笔记，音乐，文件共享，源代码管理等等。旨在尽可能自行架设所需的网络服务，不被第三方所绑架，不被一些产品经理喂💩吃。 本文大多使用开源软件，并尽可能挑选可以使用Docker的软件，来简化部署的难度。

首先说明我们为什么要自给自足？很简单，因为在线的资源不足以让我们舒服的使用。其次，一些小型的公司和组织有私密性或者性能的要求，不可以使用在线服务。

我姑且先说一下我之前也是热衷于使用各种线上服务的，我并不是完全反对线上服务，我比较多用过的包括且不限于：

* 有道云笔记
* 为知笔记
* 百度音乐盒
* 网易云音乐
* 百度网盘
* 115网盘
* Github
* Gitlab
* Circle CI
* 知乎
* cnblogs
* Slack
* QQ/Wechat...

其中有些我现在还在用，但是大多数都不用了。还在用的，有些是因为做的良心（点名：为知笔记（但是我现在不用了），Github，Gitlab，Circle CI），有些是因为没有替代品（点名：QQ，微信）。

### 线上服务的罪状

我给大家细说一下为什么现在不用某些线上服务/网站：

1. 资源限制：比如网盘，给上传不给下载，管杀不管埋，把狗用户骗进来杀，或者诸如早期的Github没有私有库（以及为了一两个私有库开pro有点。。。浪费）之类。
2. 人为制造数据孤岛：比如有道云笔记，并不能导出为诸如Word哪怕是pdf之类的格式，只进不出。各大互不联网公司互相屏蔽对手的链接，分享极为困难，出现了火星文口令这种反人类的东西。
3. 无孔不入的广告：商业公司要挣钱我可以理解，VIP还有广告就很过分，不点名了。
4. 服务降级：比如网易云音乐，开了VIP总该让我下载吧，我想用我索尼的HiFi MP3听可不可以？不可以，下载下来的格式是专有格式，需要联网才能播放。（以前没有这个问题）
5. 主动给用户添堵：最著名的就是各大厂商的网页版了，相信大家都看过“在App内打开”，“App浏览体验更佳”之类的鬼东西。其实大多数时候只是为了骗用户下载App，好监控你的隐私，以及给你推送。一不小心就跳转到App下载页面，心烦意乱，“使用桌面版”渲染的话，字体简直是视力杀手。
6. 不可抗力：我自己写的文章，有时候因为一些不可抗力会消失（肯定是知乎的存储没做好，嗯，肯定是），我并不希望它们消失。以及网易云音乐的歌曲有的时候也会因为版权的原因消失。

尽管说了这么多缺点，我仍然感谢他们提供的服务（但是还是要挥手说再见）。

同时也要说清楚的是，自给自足的线上服务大多不能完成人与人交流的功能，就算你自己建了一个类似Slack的站点，有好用的App，你不可能让你的父母亲戚都用这个Slack，还是得用微信，它们给你发“刚刚！突然宣布……”你还是得收着。

## 自给自足的方案

以下方案，随着我的使用不定期更新，也欢迎大家推荐自己喜欢的自给自足的服务。

### 网络环境

以下列举的服务，拿一台旧电脑（当然太旧的也不行）装个Linux就可以都搞定了。 但是如果这样做的话，只能在内网（家里）使用，最近疫情期间，很多人在家办公了，那么内网能用就好。

如果你有公网IP，你当然可以嚣张（我家里直接有公网IP，所以就直接建站了，我就很嚣张）。 但是国内大多数运营商给你层层NAT，到你手里都不知道自己是怎么连上的网。所以如果我们希望在办公室或者学校之类的场所使用，需要首先需要一个网络方案。

#### 购买VPS

VPS是最稳定的方案，但是因为国内几大云服务厂商的垄断，带宽卖的比金子还贵。 以阿里云为例，8M的带宽一年可以卖到4000元左右。哪怕按量付费的话，8M带宽至少是8毛钱1GB，说难听些，手机移动流量都比这个便宜了。

而诸如Vultr的国外云主机，哪怕30块钱一个月的小主机，每个月就有1TB流量，我亲测带宽可以达到100M。 但是由于用Vultr搭梯子的人太多，导致IP容易被Ban，造成误伤。 在国庆，大会以及众所周知但是不能提提了会喝茶的某月某日，IP段会被整体屏蔽。

虽然国内外云主机各有各的坑，但是VPS仍然是最稳定的建站方式，专业的IDC机房会比家里的破网络和电力情况要稳定的多，而且便宜的VPS大多数虚拟机，在硬件故障的时候，部分优秀厂商可以做到虚拟机漂移，让你对故障无感。

建议多找几个志同道合信得过的小伙伴大家合资购买一个VPS，下面推荐的服务大多是支持多账号的，一起用无论是价格还是运维（可以一人运维一个服务啊）压力都会小很多。

#### SSL

没有SSL，通讯就是在裸奔，容易被运营商灌入广告，缓存攻击，也容易被窃听。 免费的SSL证书教程参见[Let's encrypt -- 让我们一起愉快的使用HTTPS](https://github.com/TsingJyujing/blogs/tree/7faf08d843a36914a418bb44b8d6e4d214b1090a/other-tech/https/README.md)。

#### 内网穿透

VPS虽好，在家建站也有在家建站的好处，第一个好处就是数据是在家的，你可以物理的摸到的数据，虽然手上捧着硬盘并不比在IDC靠谱，但是硬件在家的安心感是无法替代的。

第一推荐的是 [ZeroTier](https://my.zerotier.com/)

这个系统不能让你有公网IP，但是却能让你跨区域组网，如果你只是在固定的几台设备上使用下列服务，那么完全可以试试异地组网。缺点也是有的，这个软件生生的造了很多概念，比如leaf，moon之类的术语，导致文档比较难懂，需要慢慢看，其次是我并不清楚国内速度和稳定性怎么样，但是我知道国内是可以用的。

其次推荐花生壳，花生壳的免费版（其实需要付6元）就已经很好用了，如果追求稳定可以买个300多一年的服务，这个钱相比VPS已经是便宜了，相比金贵的带宽性价比居然还可以。

如果你有VPS，但是VPS主机内存太小，你可以在VPS上部署内网穿透软件：[snail007/goproxy](https://github.com/snail007/goproxy/)，如果嫌管理麻烦的，可以用我写的[开源软件](https://github.com/TsingJyujing/goproxy-tunnel-server)。

### 笔记/博客系统

我目前用过最好的开源的笔记本应该是[蚂蚁笔记](https://leanote.com/)。

唯一的问题是，好久没更新了，项目可能凉了，但是App齐全，大Bug很少，平素使用绝对足够了。 还有一个缺点，用的是MongoDB，不擅长运维MongoDB的同学可能比较容易崩。

Leanote自带博客系统，但是我更喜欢用Gitbook，这篇博客就是用 Gitbook 做的。 缺点是Gitbook新版似乎国内被墙了，老版本还可以用。

如果可以花钱可以购买为知笔记，个人觉得做的是不错的了。

### Git

我目前用的是[Gogs](https://gogs.io/)，异常轻量化，只需要几十兆内存，但是功能齐全。 如果你是一个人使用或者轻度合作的话，Gogs就足够了，如果你有小团队要合作项目的话，Gitlab可能更佳。（但是Gitlab更吃资源，我印象里内存4G起步。）

目前我最推荐的还是Github，因为个人也可以无限创建私有项目了，开个Pro也不贵。不过这个不符合我们今天的主题。

### 持续集成

我前前后后用过TeamCity，Circle CI，Drone和Jenkins。

目前我用的是Drone，但是我并不推荐Drone。 主要原因是文档太少，坑太多，全靠自己看源代码折腾，我之所用Drone是因为公司在用，折腾中积累了一些经验。

如果不考虑收费，我推荐TeamCity。免费的中间比较推荐的是Jenkins，大家踩坑比较多，会好一些。

### 音乐

我目前用的是[Airsonic](https://github.com/airsonic/airsonic) 界面有点古老，App也有点过于简单，但是这已经是我能找到的最好的开源音乐软件了。

如果考虑收费的话，Subsonic还算不错，airsonic其实就是兼容Subsonic的API，所以可以直接应用Subsonic的App。

### 照片，文件和视频

照片，文件和视频，我都是靠[Nextcloud](https://nextcloud.com/)完成的：

文件的备份就不用说了。Nextcloud 的手机App可以直接帮你备份所有照片到你自己的服务器上。 更好的是，可以用WebDAV挂载，可以内部挂载别的盘（比如Samba或者SFTP协议之类的）。

网页内也可以直接播放视频，体验非常不错，不能播放的可以用ffmpeg转码成H264编码就行。

缺点是慢，而且有点臃肿，PHP写的，不好改动。

我还考察过[Seafile](https://www.seafile.com/en/home/)

三个用户以内专业版免费，支持全文搜索（ES实现的），可以说是很良心了，相比Nextcloud功能虽然不多，但是速度很快，也基本能满足使用。 可是我最后没有选它，因为照片备份不方便，这还在其次，它的文件系统格式是自定义的二进制分块存储，不方便我直接在服务器磁盘上操作使用网盘内的文件。

### TODO

其实还有很多东西我没有做到自给自足，比如Calendar，Task，我用的都是Google的产品。 也并不是所有的东西都需要自给自足，往往是在线服务的使用给了我痛苦的感觉的时候，我才会转向使用自建的服务。


# 技术备忘录

工作中乃至生活中解决了一些或大或小，或简单或复杂的技术问题。 很多都因为没有好好的记录，所以之后需要再解决一遍。 特地记录在此，以便后续使用。

## 2025-10-15 Spark的时区设置

在使用一些Spark SQL函数的时候，会需要准确的设置时区，否则执行的结果就会有误差。

比如以UTC时区执行如下SQL：

```sql
spark.sql("select from_unixtime(1760362193,'yyyy-MM-dd HH:mm:ss') as dt").show()
```

会得到：

```
+-------------------+
|                 dt|
+-------------------+
|2025-10-13 13:29:53|
+-------------------+
```

但是如果是东京时区，则是：

```
+-------------------+
|                 dt|
+-------------------+
|2025-10-13 22:29:53|
+-------------------+
```

获取时区设置：

```python3
spark.conf.get("spark.sql.session.timeZone")
```

设置时区（以东京为例）：

```python3
spark.conf.set("spark.sql.session.timeZone", "Asia/Tokyo")
```

## 批量杀线程

```
#!/bin/bash
ps -ef|grep $1|grep -v grep|awk '{print $2}'|xargs -r kill -9
```

## MySQL

### 创建用户并且授权读写Schema

```sql
create user ${USERNAME} IDENTIFIED by '${PASSWORD}';
show grants for ${USERNAME};
grant all on ${SCHEMA}.* to ${USERNAME};
flush privileges;
```

## MongoDB 权限配置

### 管理员权限配置

```javascript
db.createUser({user:"root",pwd:"123456",roles: [{ role:"root", db:"admin"}]})
```

### 用户配置样例

```javascript
db.createUser({ 
    user: "someuser",
    pwd: "somepassword",
    roles:[
        { role: "readWrite", db: "somedb" }
    ]
})
```

### 删除用户

```javascript
db.dropUser("username")
```

## MongoDB 慢查询相关

### 查找当前大于10.0秒的、且最慢的一个查询的详情

```bash
mongo --quiet --eval "let limit_time = 10.0;" slow_query.js
```

### 统计大于10秒的慢查数量

```bash
mongo --quiet --eval "db.currentOp().inprog.filter(x=>x['secs_running']>10).length"
```

一般统计大于5秒，15秒，30秒,60秒，300秒的慢查数量。

### 查询Running总时间

这个指标可以从侧面反应数据库的操作体验和压力。

```bash
mongo --quiet --eval "db.currentOp().inprog.map(x=>x.microsecs_running/1000000.0).reduce((a,b)=>a+b)"
```

```javascript
// 检查大于 limit_time 秒的慢查询数据
// let limit_time = 10.0;
// 上一句在eval里面执行来达到动态设置的效果
db.currentOp().inprog.filter(
    x=>x["microsecs_running"]>(limit_time*1000000)
).sort(
    // 取最慢的一条进行展示
    (a,b)=>{
        const va = a["microsecs_running"];
        const vb = b["microsecs_running"];
        if(a==b){
            return 0;
        }else{
            if(a>b){
                return -1;
            }else{
                return 1;
            }
        }
    }
)
```

### 杀死超时任务

```javascript
let over_time = 60.0;// Over 1 min
db.currentOp().inprog.forEach(
    opInfo=>{
        try{
            const opId = opInfo["opid"];
            const opDuration = opInfo["secs_running"];
            if(opDuration>over_time){
                db.killOp(opId);
                console.log(`Operation ${opId} killed, details: ${JSON.stringify(opInfo)}`)
            }
        }catch(err){
            console.error("Error while executing killing filter.")
            console.error(err)
        }
    }
)
```

## Python Tricks

### Lazy Evaluation

From the blog [Python 延迟初始化（lazy property）](https://segmentfault.com/a/1190000005818249)

```python
class lazy(object):
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls):
        val = self.func(instance)
        setattr(instance, self.func.__name__, val)
        return val

def lazy_property(func):
    attr_name = "_lazy_" + func.__name__

    @property
    def _lazy_property(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, func(self))
        return getattr(self, attr_name)

    return _lazy_property
```

Comparing with the lazy, it's more like cache the result, maybe can also simply be replaced by `lru_cache`. We have to ensure the return value won't change while using it.

### Example of Async wait

```python
import asyncio
from asyncio import Future
from typing import Dict

futures: Dict[str, Future] = {}


@app.get("/test/lock/{task_id}")
async def test_lock(task_id: str):
    result = asyncio.get_event_loop().create_future()
    # Put the task in Queue
    futures[task_id] = result
    return {
        "result": await result
    }


@app.get("/test/release/{task_id}")
async def test_release(task_id: str):
    if task_id not in futures:
        raise Exception("Task not found")
    # Set result while the task finished
    futures[task_id].set_result(f"{task_id} is done")
    return {
        "task": task_id
    }
```

## FFMPEG

### Convert MP4 File to MP3 File

```bash
ffmpeg -i ${MP4_FILE} -vn \
       -acodec libmp3lame -ac 2 -ab 320k -ar 48000 \
        ${MP3_FILE}
```

Replace `-ab {bitrate}` to `-ac 2 -qscale:a 4` to use VBR

### Convert to H264 mp4 file

```bash
ffmpeg -vcodec h264 -acodec copy -movflags +faststart -pix_fmt yuv420p -crf 23 -i input.mp4 output.mp4
```

### Merge subtitle (srt) file and video

```bash
ffmpeg -i ${INPUT_VIDEO} -f srt -i ${SUBTITLE_FILE} -c:v copy -c:a copy -c:s srt ${OUTPUT_VIDEO}
# -c:s can also be copy
```

## 树莓派

### RPI3 Ubuntu 摄像头

1. 只能使用32位的系统
2. 在`/boot/firmware/config.txt`里增加`start_x=1`
3. 执行 `sudo snap install picamera-streaming-demo`以后重启系统
4. 打开 http\://<树莓派IP>:8000/ 即可查看

参考资料：

* [Enable Raspberry Pi Camera in Ubuntu 20.04 MATE on Raspberry Pi 4 8GB RAM](https://www.youtube.com/watch?v=CakU8hIaP7c)
* <https://github.com/ogra1/picamera-streaming-demo>

### RPI3 Wifi

1. Edit `/etc/netplan/xxxxxx.yaml`:

```yaml
# This file is generated from information provided by
# the datasource.  Changes to it will not persist across an instance.
# To disable cloud-init's network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    ethernets:
        eth0:
            optional: true
            dhcp4: true
    # add wifi setup information here ...
    wifis:
        wlan0:
            optional: true
            access-points:
                "YOUR-SSID-NAME":
                    password: "YOUR-NETWORK-PASSWORD"
            dhcp4: true
```

```bash
sudo netplan --debug try 
# (continue even if there are errors)
sudo netplan --debug generate 
# (provides more details in case of issues with the previous command)
sudo netplan --debug apply 
# (if no issues during the previous commands)
```

Reference: [How to setup the Raspberry Pi 3 onboard WiFi for Ubuntu Server 18.04 with netplan?](https://raspberrypi.stackexchange.com/questions/98598/how-to-setup-the-raspberry-pi-3-onboard-wifi-for-ubuntu-server-18-04-with-netpla)

## Encryption

### SAML2 key/pem generator

```bash
openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -keyout encryption.key -out encryption.pem
openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -keyout signing.key -out signing.pem
```

## K8S

### 创建一个管理员账户的配置文件

首先创建一个Service Account

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: <Account Name>
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: <Account Name>
  namespace: default
```

获取账户详情 `kubectl describe serviceaccount <Account Name>`

随后获取secret的名字，格式一般是 `<Account Name>-token-<随机字符串>`

最后获取Token：

```bash
kubectl describe secret <Token Secret Name>
```

```yaml
apiVersion: v1
kind: Config
users:
- name: <Account Name>
  user:
    token: "<TOKEN>"
clusters:
- name: <Cluster Name>
  cluster:
    api-version: v1
    server: <API ENDPOINT>
contexts:
- name: <Cluster Name>
  context:
    cluster: <Cluster Name>
    user: <Account Name>
current-context: <Cluster Name>
```

## Java/Scala/Kotlin

### Maven Settings

Mainly `~/.m2/settings.xml`.

```markup
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">

  <servers>
    <server>
      <id>nexus-snapshots</id>
      <username>{username}</username>
      <password>{password}</password>
    </server>
    <server>
      <id>nexus-releases</id>
      <username>{username}</username>
      <password>{password}</password>
    </server>
  </servers>

  <mirrors>
    <mirror>
      <id>central</id>
      <name>central</name>
      <url>https://nexus.xxxx.com/repository/maven/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>

</settings>
```

## Glances

### 屏蔽多余的设备

在使用snap的时候经常会有一些奇怪的设备，包括`loop{x}`或者是`/snap/`之类的，这个时候只要在配置文件里加上

```
[fs]
hide=/boot.*,/snap.*
[diskio]
hide=loop.*
[network]
hide=docker.*,lo,veth.*
```

就可以屏蔽这些设备。

官方文档：

1. <https://glances.readthedocs.io/en/latest/aoa/diskio.html>
2. <https://glances.readthedocs.io/en/latest/aoa/fs.html>
3. 配置文件的位置 <https://glances.readthedocs.io/en/latest/config.html>

## Git

### 自动使用SSH代替HTTP

来源：<https://www.digitalocean.com/community/tutorials/how-to-use-a-private-go-module-in-your-own-project>

在`~/.gitconfig`加入这一段：

```ini
[url "ssh://git@github.com/"]
insteadOf = https://github.com/
```

不仅可以用于导入私有的Go模块，还可以用于Brew安装的时候自动拉取Repo。


# 用Rust开发AVR单片机

这个是失败的例子

## 环境准备

安装Rust，这一部分参考：<https://github.com/avr-rust/rust>

```bash
# FROM https://www.rust-lang.org/tools/install
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Enable it:

source $HOME/.cargo/env

# Add AVR toolchain:
git clone https://github.com/avr-rust/rust.git --recursive
```

安装rust的时候一定记住不能选完全模式（complete）否则会导致之后旧版本无法安装。

整个项目这样clone下来大约3.8G……嗯……准备永久收藏了。

然后是build整个项目：

```bash
# Grab the avr-rust sources
git clone https://github.com/avr-rust/rust.git --recursive

# Create a directory to place built files in
mkdir build && cd build

# Generate Makefile using settings suitable for an experimental compiler
../rust/configure \
  --enable-debug \
  --disable-docs \
  --enable-llvm-assertions \
  --enable-debug-assertions \
  --enable-optimize \
  --enable-llvm-release-debuginfo \
  --experimental-targets=AVR \
  --prefix=/usr/local/avr-rust

# Build the compiler, install it to /usr/local/avr-rust
make
make install
```

make和make install这两步可能耗费数小时，需要十足的耐心。

就我的记录而言，编译的相当惨烈啊……

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-87d0cf24755c27b601b1eb0b1407bb1e05ee40e0%2F2020-07-18-23-49-41.png?alt=media)

最后安装就好了：

```bash
# Register the toolchain with rustup
# MAC
rustup toolchain link avr-toolchain /usr/local/avr-rust
# Linux
rustup toolchain link avr-toolchain $(realpath $(find . -name 'stage2'))

# Optionally enable the avr toolchain globally
rustup default avr-toolchain
```

这个时候切换旧版本：`rustup toolchain install 1.37.0`

然后准备安装xargo，也不能直接安装，需要clone源代码切换到0.3.17版本。

```bash
git clone https://github.com/japaric/xargo.git
git checkout tags/v0.3.17
cargo install --path ./ --locked
```

目前为止，仍然失败，暂时放弃。


# React-Script转Vite时引用路径的问题

最大的问题是Import Path的问题，正常情况下，如果我有这样的src：

```
src
├── Config.ts
├── ThemeProvider.tsx
├── api
├── assets
│   └── react.svg
├── component
│   ├── CopyText.tsx
│   └── RequiresHttps.tsx
├── i18n.ts
├── index.css
├── locales.json
├── main.tsx
├── view
│   ├── ErrorPage.tsx
│   └── Login.tsx
└── vite-env.d.ts
```

那么，我只要在`tsconfig.json`里的`compilerOptions`添加`"baseUrl": "./src"`，那么我在`main.tsx`里面可以以如下的形式引用：

```tsx
import ThemeProvider from "ThemeProvider";
// Also can be
import ThemeProvider from "./ThemeProvider";
```

但是，现在第一种方式用不了了，或许VSCode还能正常识别，但是用vite启动服务器的时候就会找不到包。

这个时候只要安装这个包到dev：`https://github.com/aleclarson/vite-tsconfig-paths`

随后把配置换成这个：

```ts
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  // 本来是：plugins: [react()],
  plugins: [tsconfigPaths()],
})
```

就可以自动的从`tsconfig.json`里面导入path，这样一来，就不用写[resolve.alias](https://vitejs.dev/config/shared-options.html#resolve-alias)了。

因为毕竟写alias，我看大多数的方案也是做成这样：

```ts
// vite.config.ts
{
  resolve: {
    alias: [
      { find: '@', replacement: path.resolve(__dirname, 'src') },
    ],
  },
  // ...
}
```

导入的时候就是：

```tsx
import ThemeProvider from "@/ThemeProvider";
```

我仍然不太喜欢，而且对于老项目，需要批量更改import着实是个负担。


# 量化交易

自己（在日本）搭建量化交易系统玩儿的一些经验。还在写，所以暂时不在blog上开放，但是Github可以看到。


# 零零碎碎的技术文章

数据库，网络，Linux等等工作生活中积累的技术文章。


# 数据库技术


# 记一个离谱的MySQL语句的性能问题

## 故障现象

我司有一个系统里存在某个SQL，定期会需要把一些数据标记为删除，这个SQL是这样写的：

```sql
-- 注：本文的SQL都进行了脱敏处理，但是绝对不影响理解
UPDATE production_embedding_map
SET deleted = true
WHERE production_id IN (SELECT production_id FROM t_delete_production_ids);
```

Beta环境里测试过了后，就扔到线上去跑了（当时开发的急，只有做功能测试，性能测试则没有做）。 结果有一天，突然告警，说任务超时了，然后就被自动干掉重新执行，但是重试了几十次仍然是失败的。

## 故障重现

看到这个SQL的第一反应就是`IN`写的不好（因为已经确认过，`production_id`字段是有索引的），这个没什么证据，就是SQL写多了的直觉（但是仍然写出了这样的SQL，丢人啊）。

于是我跑到线上去下载了一份生产数据，本地（其实是另一个服务器）用Docker起了一个MySQL8实例，然后把数据导入本地进行测试。

打开一个SQL会话，然后随机抽取一些数据来模拟标记删除的过程：

```sql
create temporary table t_delete_production_ids
(
    production_id INT UNSIGNED
);

insert into t_delete_production_ids (production_id)
select production_id
from (select distinct production_id
      from production) eids
order by RAND()
limit 3000;
```

然后执行该SQL，期待该SQL直接卡死，然而发现完！全！没！问！题！！

多执行了几次发现的确没问题以后，怀疑是MySQL小版本的问题，线上跑的是MySQL 8.0.19而我Docker里面拉的是最新的8.0.30。 使用二分法最小版本进行测试后发现，该问题在8.0.21被修复了。

## 分析与修复

其实问题出在执行计划上，如果用EXPLAIN解释原始的SQL，可以看到8.0.20及以前的执行计划是：

| id | select\_type       | table                      | partitions | type  | possible\_keys | key     | key\_len | ref  | rows    | filtered | Extra       |
| -- | ------------------ | -------------------------- | ---------- | ----- | -------------- | ------- | -------- | ---- | ------- | -------- | ----------- |
| 1  | UPDATE             | production\_embedding\_map | null       | index | null           | PRIMARY | 4        | null | 6201234 | 100      | Using where |
| 2  | DEPENDENT SUBQUERY | t\_delete\_production\_ids | null       | ALL   | null           | null    | null     | null | 2818    | 10       | Using where |

如果更新到8.0.21及以后，哦，它把SUBQUERY的结果物化了，然后就变成了和INNER JOIN（最后的解决方案）一样的执行计划（MATERIALIZED这一步骤的执行成本可忽略）。

| id | select\_type | table                      | partitions | type | possible\_keys | key            | key\_len | ref                         | rows | filtered | Extra       |
| -- | ------------ | -------------------------- | ---------- | ---- | -------------- | -------------- | -------- | --------------------------- | ---- | -------- | ----------- |
| 1  | SIMPLE       | \<subquery2>               | null       | ALL  | null           | null           | null     | null                        | null | 100      | Using where |
| 1  | UPDATE       | production\_embedding\_map | null       | ref  | production\_id | production\_id | 5        | \<subquery2>.production\_id | 44   | 100      | null        |
| 2  | MATERIALIZED | t\_delete\_production\_ids | null       | ALL  | null           | null           | null     | null                        | 3311 | 100      | null        |

可是我们没法升级MySQL的版本，所以只能绕开它，让MySQL生成和新版一样的执行计划。不讨论Optimizer的骚操作，一般SQL如果在IN上遇到瓶颈，最好的办法是换成JOIN，如果换成INNER JOIN，执行计划就是：

```sql
UPDATE production_embedding_map pem
INNER JOIN t_delete_production_ids tdpi ON pem.production_id = tdpi.production_id
SET deleted = true
WHERE tdpi.production_id is not null;
```

| id | select\_type | table | partitions | type | possible\_keys | key            | key\_len | ref                                                   | rows | filtered | Extra       |
| -- | ------------ | ----- | ---------- | ---- | -------------- | -------------- | -------- | ----------------------------------------------------- | ---- | -------- | ----------- |
| 1  | SIMPLE       | tdpi  | null       | ALL  | null           | null           | null     | null                                                  | 2818 | 90       | Using where |
| 1  | UPDATE       | pem   | null       | ref  | production\_id | production\_id | 5        | duplicated\_production\_detection.tdpi.production\_id | 46   | 100      | null        |

## 所以MySQL到底更新了什么

其实MySQL的更新说来也简单，就是让优化器变得更加聪明一点，会物化子查询了：[Changes in MySQL 8.0.21 (2020-07-13, General Availability) Optimizer Notes](https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-21.html#mysqld-8-0-21-optimizer)

> A single-table UPDATE or DELETE statement that uses a subquery having a \[NOT] IN or \[NOT] EXISTS predicate can now in many cases make use of a semijoin transformation or subquery materialization.

## 这个故事告诉我们什么

* Beta/测试环境的所有版本最好都和生产做到小版本一致
* 对于赶工的项目不可能逐一确认，但是保险起见，所有的SQL最好都扫一眼执行计划
  * 我觉得更好的方式是封装执行SQL的部分，仅在Beta环境输出Explain的结果
* 不要完全相信Optimizer


# 为PostgreSQL配置SSL加密

## 为什么要为PostgreSQL配置SSL加密

如果你的服务只是在内网使用，那么数据库大多是不需要加密的（或者自签名加密也可以），但是如果你的数据要跨区域传递，而且你也没有相应的措施来确保你的流量是加密的，那么对数据库本身加密就显得特别重要了。

说到我的情况，我是为了把我的数据库共享给我的朋友用，考虑到数据量确实不小，我采取了下面两个措施：

1. 家庭的端口直接开放（配合DDNS）暴露数据库端口；
2. 数据定期打包备份到RustFS，配合Google OAuth，朋友用Google账户登录就可以直接访问数据。

最危险的其实是1，考虑到数据安全，必须用SSL加密。

## 用什么证书进行加密？

> \[!TIP] 如果对HTTPS和Let's encrypt还不熟悉的，建议先阅读这篇文章：[Let's encrypt -- 让我们一起愉快的使用HTTPS](/tech/network/https)

一般来说，我们有自签名和标准的CA证书两种。我这里是推荐CA证书的。 很多人可能会想，用一下自签名证书差不多就得了，用CA签发的证书多少有点小题大做了。但是这有可能遭到中间人攻击，数据还是会被解密。 既然是做安全，那就贯彻到底吧！

细说一下的话，PostgreSQL的SSL证书验证有以下几种方式：

1. require：有证书就行，无条件相信对方的证书
2. verify-ca：只要是CA签发的证书就可以，不看主机名是不是匹配
3. verify-full：在验证证书链之后，还会严格检查服务器证书中的主机名（CN 或 SAN）是否与客户端连接时指定的主机名完全匹配。

我这里用的是`verify-full`，这可以有效防止中间人攻击，因为攻击者必须拿到与你所连主机名完全匹配的证书才行，而受信任的 CA 不会轻易签发这种证书。

## 本文使用的环境

本文使用PostgreSQL 18进行配置：

```yaml
services:
  db:
    image: postgres:18
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: "your-password"
      POSTGRES_DB: your_db
    ports:
      - "5432:5432"
    volumes:
      - ./pgdata:/var/lib/postgresql #数据
      - ./init:/docker-entrypoint-initdb.d #初始化的时候执行的脚本
      - ./backup:/backup #用于输出备份文件
      - ./letsencrypt:/etc/letsencrypt #kavehbc/free-ssl-cloudflare输出的证书会定期同步到这里
      - ./ssl:/etc/ssl/ #实际Postgresql使用的证书
      - ./scripts:/scripts #一些脚本
```

## 如何配置SSL加密

### 配置密钥文件

首先编辑`/var/lib/postgresql/18/docker/postgresql.conf`

找到下面三行做如下配置：

```ini
ssl = on
ssl_key_file = '/etc/ssl/server.key'
ssl_cert_file = '/etc/ssl/server.crt'
```

其次，定期复制和Reload配置的脚本如下：

```bash
cp /etc/letsencrypt/live/your-domain.com/fullchain.pem /etc/ssl/server.crt
cp /etc/letsencrypt/live/your-domain.com/privkey.pem  /etc/ssl/server.key
chown postgres:postgres /etc/ssl/server.crt /etc/ssl/server.key
chmod 600 /etc/ssl/server.key
chmod 644 /etc/ssl/server.crt
psql -U postgres -c "SELECT pg_reload_conf();
```

保存到scripts里面，用cronjob定期执行。之所以不直接使用letsencrypt文件夹下的密钥是因为文件不对，我不希望乱动权限所以就干脆复制一份了。

其次，PosgreSQL对文件的权限也有要求，权限太宽也不给过，所以这里用复制是坠吼的。

### 要求数据库连接加密

其次编辑`/var/lib/postgresql/18/docker/pg_hba.conf`，我的大方针是：localhost来的连接不需要鉴权，方便我执行一些操作（默认就是如此，不要改动）。

内网来的连接需要验证密码但是数据不用SSL加密：

```
host all all 192.168.1.0/24 scram-sha-256
```

其它所有的连接都需要验证密码+SSL加密：

```
hostssl	all	all	all	scram-sha-256
```

这里设定为hostssl就是需要SSL的意思。

## 如何使用

服务器端其实才不管有的没的，反正来了非内网的连接就用SSL加密通讯，账户密码验证过了就给用。

安全的设置是在客户端的，我在pgAdmin4设置了如下参数：

* `sslmode`: `verify-full`
* `sslrootcert`: `system`

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e54b2250ed60fa5fb68750a6fa91bccfda2ec961%2F2026-05-08-00-10-45.png?alt=media)

这样就会完整的校验服务器的证书是不是有效再决定是否通信了。

### Python(psycopg3)

为了确保我们有所有CA的根证书，首先安装[certifi](https://pypi.org/project/certifi/)包。

随后连接的时候记得加上：

```py
conn: Connection = psycopg.connect(
    conninfo=db_uri,
    sslmode="verify-full",
    sslrootcert=certifi.where(),
)
```

这样就连上了。


# FFMPEG


# 为FFMPEG添加Intel QSV支持

## 简介

使用CPU转码的速度过于慢，在使用12核心的i7-10710U CPU全力转码也只有75fps的情况下，使用自带的Mesa Intel® UHD Graphics (CML GT2)进行转码在CPU占用率只有1/3的情况下，就可以达到400+fps的转码速度，速度提升了6倍。

但是直接使用`apt-get`安装的ffmpeg是不带QSV支持的，因为QSV并不是开源的，所以只有自己编译了。

如果完全按照[Intel® Quick Sync Video Installation](https://www.intel.com/content/www/us/en/architecture-and-technology/quick-sync-video/quick-sync-video-installation.html)的指示安装，恐怕是要过错年的。文档的格式排版辣眼睛不说，而且连术语都变了，文档让你安装的MediaServerStudio，早已经变成了MediaSDK。

### 2026-02-17更新

这个年是彻底要过错了，我重新安装了系统，准备编译带QSV的ffmpeg，结果[MediaSDK也被废弃了](https://www.intel.com/content/www/us/en/docs/onevpl/upgrade-from-msdk/2023-1/overview.html)，现在改了个狗屁名叫甚么[Intel Video Processing Library (Intel VPL)](https://www.intel.com/content/www/us/en/developer/tools/vpl/overview.html)。不过好消息是，我在Debian下安装的ffmpeg默认支持基于libvpl的QSV编解码器了，libvpl也可以从apt直接安装了。这样就省去了我很多的事儿了。我认为这是做了一件人事，值得鼓励。

所以以后只需要参考使用的部分即可，至于安装的部分，我决定留在这里，作为某大公司产品因为拍脑袋引起的产品设计混乱的一个见证。

### 2026-09-22更新

我上面还是写的简单了，让我再补充一下吧。

首先要确认你的ffmpeg是有qsv编码器的：

```
# ffmpeg -hide_banner -encoders | grep qsv 
 V..... av1_qsv              AV1 (Intel Quick Sync Video acceleration) (codec av1)
 V..... h264_qsv             H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration) (codec h264)
 V..... hevc_qsv             HEVC (Intel Quick Sync Video acceleration) (codec hevc)
 V..... mjpeg_qsv            MJPEG (Intel Quick Sync Video acceleration) (codec mjpeg)
 V..... mpeg2_qsv            MPEG-2 video (Intel Quick Sync Video acceleration) (codec mpeg2video)
 V..... vp9_qsv              VP9 video (Intel Quick Sync Video acceleration) (codec vp9)
```

然后你需要一些妙妙小依赖（记得apt update再安装）

```shell
apt install intel-media-va-driver-non-free libvpl-tools
```

至于为什么要non-free，我搜索了一下大概是这个解释：

> 这源于 Debian 对软件自由的严格定义。“non-free” 并不意味着需要付费，而是指软件包中包含了不可读的二进制代码（binary blobs）。Intel 的显卡驱动为了提高效率，部分着色器代码以二进制形式提供，这不符合 Debian 的“自由”标准，因此被拆分到 non-free 仓库中。

最重要的还是得安装libmfx，如果你是11代及之后的CPU的话，可以安装 `libmfx-gen1.2`，但是我的XPS是几年前买的，还是10代CPU，不支持什么oneVPL，所以需要安装下面的过时的包（Intel你做个人吧）：

```shell
wget https://deb.debian.org/debian/pool/main/i/intel-mediasdk/libmfx1_22.5.4-1_amd64.deb
sudo apt install ./libmfx1_22.5.4-1_amd64.deb
```

最后可以简单跑一个测试指令：

```shell
ffmpeg -f lavfi -i testsrc=size=1280x720:rate=30 -t 5 -c:v h264_qsv -f null -
```

没有Conversion failed!那就是可以了。

余下的部分都是历史了。

***

## 安装Media SDK

首先到Github上下载MediaSDK：<https://github.com/Intel-Media-SDK/MediaSDK/releases>

当然按照Intel的尿性，下载的位置可能有变化，找不到的时候就参考一下官方页面：[Intel® Media SDK](https://www.intel.com/content/www/us/en/developer/tools/media-sdk/overview.html)。

随后解开压缩包，并且执行其中的安装脚本：

```bash
tar -xzvf MediaStack.tar.gz
cd MediaStack
chmod +x install_media.sh
sudo ./install_media.sh
```

这第一步就算完了。

## 编译FFMPEG

Intel文档里的这一部分不能说完全正确吧，只能说错漏百出。这里参考ffmpeg的官方的Wiki比较好。

首先安装需要的包：

```bash
sudo apt-get update -qq && sudo apt-get -y install \
  autoconf \
  automake \
  build-essential \
  cmake \
  libass-dev \
  libfreetype6-dev \
  libgnutls28-dev \
  libmp3lame-dev \
  libsdl2-dev \
  libtool \
  libva-dev \
  libvdpau-dev \
  libvorbis-dev \
  libxcb1-dev \
  libxcb-shm0-dev \
  libxcb-xfixes0-dev \
  meson \
  ninja-build \
  pkg-config \
  texinfo \
  wget \
  yasm \
  zlib1g-dev \
  libunistring-dev \
  libaom-dev \
  git-core libdav1d-dev
```

官网文档要求安装这些包，但是我的Ubuntu 20.04找不到`libdav1d-dev`所以干脆不装了，不影响QSV的使用，git-core也可以不用安装，不过我的机器上已经安装了git了。

之后下载源代码并且生成配置：

```bash
wget -O ffmpeg-snapshot.tar.bz2 https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2
tar xjvf ffmpeg-snapshot.tar.bz2
cd ffmpeg
./configure --enable-libmfx --enable-nonfree | tee config.out
```

注意，由于我们可能不止使用qsv的ffmpeg，所以一个比较实用的命令会是：

```
./configure --enable-libmfx --enable-nonfree --enable-gpl --enable-libx264 --enable-libx265 --enable-libvpx --enable-libopus --enable-libsvtav1 | tee config.out
```

这个时候可能会提示：`ERROR: libmfx not found`。

参考[这个ISSUE](https://github.com/Intel-Media-SDK/MediaSDK/issues/1822)

只需要`export PKG_CONFIG_PATH=/opt/intel/mediasdk/lib/pkgconfig/`就可以了。

实际情况是，我的电脑是64位，所以应该是：`export PKG_CONFIG_PATH=/opt/intel/mediasdk/lib64/pkgconfig`。

最后直接make就行：

```bash
make -j 12
```

编译完看一眼支持的codec是不是正确：

```bash
./ffmpeg -codecs|grep qsv
```

正确的话应该输出类似：

```
ffmpeg version N-105268-g8b9ef5a516 Copyright (c) 2000-2022 the FFmpeg developers
  built with gcc 9 (Ubuntu 9.3.0-17ubuntu1~20.04)
  configuration: --enable-libmfx --enable-nonfree
  libavutil      57. 18.100 / 57. 18.100
  libavcodec     59. 20.100 / 59. 20.100
  libavformat    59. 17.100 / 59. 17.100
  libavdevice    59.  5.100 / 59.  5.100
  libavfilter     8. 25.100 /  8. 25.100
  libswscale      6.  5.100 /  6.  5.100
  libswresample   4.  4.100 /  4.  4.100
 D.V.L. av1                  Alliance for Open Media AV1 (decoders: av1 av1_qsv )
 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_v4l2m2m h264_qsv ) (encoders: h264_qsv h264_v4l2m2m h264_vaapi )
 DEV.L. hevc                 H.265 / HEVC (High Efficiency Video Coding) (decoders: hevc hevc_qsv hevc_v4l2m2m ) (encoders: hevc_qsv hevc_v4l2m2m hevc_vaapi )
 DEVIL. mjpeg                Motion JPEG (decoders: mjpeg mjpeg_qsv ) (encoders: mjpeg mjpeg_qsv mjpeg_vaapi )
 DEV.L. mpeg2video           MPEG-2 video (decoders: mpeg2video mpegvideo mpeg2_v4l2m2m mpeg2_qsv ) (encoders: mpeg2video mpeg2_qsv mpeg2_vaapi )
 D.V.L. vc1                  SMPTE VC-1 (decoders: vc1 vc1_qsv vc1_v4l2m2m )
 DEV.L. vp8                  On2 VP8 (decoders: vp8 vp8_v4l2m2m vp8_qsv ) (encoders: vp8_v4l2m2m vp8_vaapi )
 DEV.L. vp9                  Google VP9 (decoders: vp9 vp9_v4l2m2m vp9_qsv ) (encoders: vp9_vaapi vp9_qsv )
```

## 测试

编译出来的ffmpeg就可以直接用了，这时我测试的指令：

```bash
./ffmpeg -i input.mp4 -init_hw_device qsv=hw -filter_hw_device hw -vf hwupload=extra_hw_frames=64,format=qsv -c:v h264_qsv -maxrate 5M -movflags +faststart output.mp4
```

在实际测试的过程中，一些选项是不能用的，比如`-pix_fmt yuv420p -crf 23`这一类的，去掉就可以。

偶尔会遇到像素格式不对的问题，需要设置如此设置 `-vf hwupload=extra_hw_frames=64,scale_qsv=format=nv12`。 但是在实际测试中，这样的转码速度会下降，不知道是不是像素格式引起的。

## 参考资料

* [FFMPEG wiki Hardware/QuickSync](https://trac.ffmpeg.org/wiki/Hardware/QuickSync)
* [Compile FFmpeg for Ubuntu, Debian, or Mint](https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu)
* [英特尔® 快速视频同步 (Quick Sync Video) 技术-英特尔® 官网](https://www.intel.cn/content/www/cn/zh/architecture-and-technology/quick-sync-video/quick-sync-video-general.html)
* [Intel® Quick Sync Video Installation](https://www.intel.com/content/www/us/en/architecture-and-technology/quick-sync-video/quick-sync-video-installation.html)
* [Intel® Media SDK](https://www.intel.com/content/www/us/en/developer/tools/media-sdk/overview.html)


# 视频压制技术笔记

## 不同视频网站的不同标准

* [Youtube](https://support.google.com/youtube/answer/1722171)

## Youtube

* Progressive scan (no interlacing)
  * Default: <https://blog.p2hp.com/archives/7328>
* High Profile
* 2 consecutive B frames
  * <https://www.reddit.com/r/ffmpeg/comments/g708bz/show\\_the\\_consecutive\\_bframes\\_before\\_encoding/>
  * <https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping>
* Closed GOP. GOP of half the frame rate.
  * Default: <https://streaminglearningcenter.com/blogs/open-and-closed-gops-all-you-need-to-know.html>
* CABAC
  * Default: <https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping>
* Variable bitrate. No bitrate limit required, though we offer recommended bit rates below for reference
  * Default
* Chroma subsampling: 4:2:0
  * <https://trac.ffmpeg.org/wiki/Chroma%20Subsampling>

Test commands:

```bash
ffmpeg -hwaccel_output_format qsv -hwaccel qsv -i input.mp4 -c:v h264_qsv -bf 2 -b:v 6M -maxrate 24M -global_quality 25 -look_ahead 1 -pix_fmt yuv420p output.mp4
ffmpeg -i input.mp4 -c:v libx264 -bf 2 -b:v 7M -maxrate 24M -crf 30 -movflags +faststart -pix_fmt yuv420p output.mp4
# Youtube compress
ffmpeg -i input.mp4 -vf yadif,format=yuv422p -force_key_frames 'expr:gte(t,n_forced/2)' -c:v libx264 -crf ${CRF_VALUE} -maxrate 12M -bf 2 -c:a aac -ac 2 -ar 44100 -use_editlist 0 -movflags +faststart output.mp4
```

## Bilibili

## References

* <https://trac.ffmpeg.org/wiki/Encode/H.264>


# Linux


# 修复Linux磁盘中的坏道

## 修复Linux磁盘中的坏道

### 前言

我之所以写这篇文章，是因为我最近在尝试修复一台十几年前的老笔记本（电脑的型号是联想的G470）。 我用了一块，大概几年前买的1TB的西部数据的SMR烂磁盘，和我另一台坏电脑上拆下来的2x8G的内存条来修复它。 老马拉破车，修复以后也不会让它跑太重的任务，可能也就跑一些爬虫或者定时任务的脚本，以Stateless的任务为主。再坏掉的话我可能会丢掉这台电脑，毕竟这玩意儿也就比树莓派强一些。

于是乎我在这台老电脑上装了最新的Debian操作系统，配XFCE轻量界面，即使是这样的界面，我也默认关闭了，平时启动只会启动命令行。 这里唯一的问题是这个磁盘，它有一些坏道，一旦操作系统读写到了这些坏道就有可能死机，本着没坏就往死里用的精神，我需要屏蔽这些坏道使用。

**注意**：正常的电脑请不要模仿这里的操作，如果磁盘到寿命了，应该尽快更换新的磁盘（哪怕它还能用）。

关于硬盘相关的内容，请看我以前的文章：[构建家用NAS过程中的碎碎念](https://blog.tsingjyujing.com/other-tech/nas#ying-pan-hdd)

### 方法

先`sudo fdisk -l`一下确认我有哪些硬盘：

```
Disk /dev/sda: 931.51 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: WDC WD10SPZX-22Z
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: dos
Disk identifier: 0x826de920

Device     Boot      Start        End    Sectors   Size Id Type
/dev/sda1  *          2048 1920008191 1920006144 915.5G 83 Linux
/dev/sda2       1920010238 1953523711   33513474    16G  f W95 Ext'd (LBA)
/dev/sda5       1920010240 1953523711   33513472    16G 82 Linux swap / Solaris

Partition 2 does not start on physical sector boundary.
```

其中sda1是主分区，sda2是扩展分区，下面包含sda5这个swap逻辑分区。 这里可以看到`Partition 2 does not start on physical sector boundary.`是因为我装debian的时候偷懒，没有手动调整分区而是让安装器自己去分了。

#### 进入Rescue模式

这个时候推荐用Debian的安装盘（U盘）启动另一个系统，因为修复坏扇区的时候要求系统盘**不被挂载**。

#### 修复主分区

主分区是EXT文件系统，这个时候我们可以用Debian自带的badblocks来扫描坏道（没有的话可以试试安装`sudo apt install e2fsprogs`）:

```shell
sudo badblocks -v /dev/sda1 > badblocks-sda1.txt
```

这个扫描可能运行很久，最好放着跑一晚上。

我稍微数了一下，已经有56个坏道，可见硬盘已经到寿命了，这个电脑上运行的任何东西都需要随时备份，**做好随时崩溃的准备**。

修复SDA1（将坏道标记为不可用）

```shell
sudo e2fsck -l badblocks-sda1.txt /dev/sda1
```

别的系统可以用`fsck`，它本质上是个代理，会调用合适的程序去处理相应的文件系统。

#### 查看磁盘健康情况

```shell
sudo apt install smartmontools
```

#### 修复SWAP分区

修复SWAP的时候就不需要Rescue模式下了，因为SWAP可以关闭再打开而不会影响系统和运行。

SWAP分区也在同一块硬盘上，自然也会有相应的问题。

1. 首先关闭所有SWAP分区：`sudo swapoff -a`
2. 随后创建新的SWAP分区：`sudo mkswap -c /dev/sda5`
   * 其中参数c的意思是check这个磁盘，跳过有问题的扇区
3. 最后加载回来：`sudo swapon -a`

有的时候如果是外部修改，硬件的UUID可能变化而加载不上，可以试试看`lsblk -f`列出所有的硬件的UUID，然后手动修改`/etc/fstab`

## 参考资料

* <https://www.tecmint.com/check-linux-hard-disk-bad-sectors-bad-blocks/>
* <https://www.man7.org/linux/man-pages/man8/mkswap.8.html>


# 2026年3月15日 搭建Linux服务器时候的备忘录

修复一台老电脑，使之能勉强使用的时候的一些碎碎念。

## 图形界面和CLI

### 启动的时候不要启动图形界面，而是CLI

```shell
sudo systemctl set-default multi-user.target
```

### 恢复图形界面的使用

```shell
sudo systemctl set-default graphical.target
```

### 手动临时启动图形界面

```shell
sudo systemctl start display-manager
```

或者 `startx` （我没试过）

如果之后要退回CLI但是不想重启的话： `sudo systemctl stop display-manager`

## 合盖时候的行为

`sudo vim /etc/systemd/logind.conf`

其中Lid相关的部分是合盖的操作：

```
#HandleLidSwitch=suspend
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
```

## swap相关设置

### swap file

如果是固态硬盘，推荐不要单独设置SWAP分区，而是使用SWAP File，这样调整起来比较灵活一点。

假设当前电脑内没有任何SWAP分区：

```shell
# 按照你的内存大小去创建即可，一般和内存一样大
sudo fallocate -l 16G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile

# 立刻挂载
sudo swapon /swapfile
sudo swapon --show
```

如果需要启动的时候自动挂载，编辑文件：`sudo vim /etc/fstab`

添加: `/swapfile none swap sw 0 0`

### swapiness设置

一般来说，SWAP的速度总是不如正经内存的，如果过于积极的使用SWAP会降低系统的性能，所以我们把这个数值调整的小一些：

* 查看数值：`cat /proc/sys/vm/swappiness`
* 临时更改：`sudo sysctl vm.swappiness=10`
* 永久设置：`sudo vim /etc/sysctl.d/99-custom.conf` 添加：`vm.swappiness=10`
  * 注：`/etc/sysctl.d/99-custom.conf`只是一个例子。

### zram

如果系统的CPU有富余，可以设置zram，就是将一部分内存进行压缩，用CPU换内存空间。

其实Mac也会干这个事情，只是已经默认集成到了系统中不需要设置罢了。

```shell
sudo apt install zram-tools
sudo systemctl enable zramswap
sudo systemctl start zramswap
```

启用以后可以看到：`swapon --show`

```
swapon --show
NAME       TYPE      SIZE   USED PRIO
/swapfile  file       16G 379.3M   -2
/dev/zram0 partition 7.7G 539.8M  100
```

## 安装软件

### 常用软件

```shell

sudo apt install -y \
    tree htop ncdu git vim zsh wget curl \
    keyd keyd-application-mapper \
    ffmpeg pipx ibus-mozc ibus-pinyin \
    build-essential cmake intel-gpu-tools


pipx install "glances[all]"
pipx install ruff
pipx install uv
```

需要把\~/.local/bin添加到PATH里面去，我是zsh所以修改\~/.zshrc。

### Docker

没什么说的，按照步骤操作就行了：<https://docs.docker.com/engine/install/debian/#install-using-the-repository>

### Node Exporter

家里别的地方搭了整套的监控系统（Grafana/Prometheus之类的），需要安装Node Exporter。

用Docker Compose是最方便的，但是我希望能不受Docker的影响，所以我选择下载二进制文件安装。

首先下载并且把程序解压到 /usr/local/bin 里面去（或者任意你想要的路径）：<https://github.com/prometheus/node\\_exporter/releases>

创建相应的Service文件：`/etc/systemd/system/node_exporter.service`

```ini

[Unit]
Description=Node Exporter
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/node_exporter \
    --collector.logind --collector.processes --collector.systemd --collector.swap

Restart=on-failure

[Install]
WantedBy=multi-user.target
```

```shell
# 加载文件变更
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start node_exporter
# 设置自动启动
sudo systemctl enable node_exporter
```

如果修改了Service文件

```shell
# 加载文件变更
sudo systemctl daemon-reload
# 重新启动服务
sudo systemctl restart node_exporter
```


# 在Linux读取并且监控APC UPS的状态

我买了一台[APC 無停電電源装置 UPS 常時商用給電 550VA/330W BE550M1-JP 矩形波 家庭用](https://www.se.com/jp/ja/product/BE550M1-JP/apc-ups-es%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E5%8D%98%E7%9B%B8100v-550va-330w-%E5%B8%B8%E6%99%82%E5%95%86%E7%94%A8%E7%B5%A6%E9%9B%BB%E6%96%B9%E5%BC%8F-%E7%96%91%E4%BC%BC%E6%AD%A3%E5%BC%A6%E6%B3%A2-%E3%82%B5%E3%83%BC%E3%82%B8%E4%BF%9D%E8%AD%B7%E4%BB%98%E3%81%8D-3%E5%B9%B4%E4%BF%9D%E8%A8%BC/)。

这么几年我一直都是靠一台Windows上的软件监控的，但是这种每次都要通过眼睛去看的方案并不是特别方便。而且也没有办法查看历史数据。 今天搜索了一下，其实是有linux的方案的。特别在此记录一下 。

## APC UPS 连接

我的UPS是通过USB和电脑进行连接的，接好线缆以后执行下面的指令可以看到设备：

```
$lsusb | grep "American Power"
Bus 002 Device 009: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
```

就说明有希望。

安装相应的软件：

```shell
sudo apt update
sudo apt install apcupsd
```

修改配置文件 `/etc/apcupsd/apcupsd.conf`

确保UPSCABLE是usb，UPSTYPE是usb，DEVICE留空（但是不要注释），类似这种感觉：

```
UPSCABLE usb
UPSTYPE usb
DEVICE
```

修改配置文件：`/etc/default/apcupsd` 为

```
ISCONFIGURED=yes
```

启动服务，设置自启动：

```
sudo systemctl start apcupsd
sudo systemctl enable apcupsd
```

这个时候执行 `apcaccess status` 就可以看到UPS的状态了。

类似下面这样：

```
APC      : 001,036,0861
DATE     : 2026-05-02 00:05:36 +0900  
HOSTNAME : g470
VERSION  : 3.14.14 (31 May 2016) debian
UPSNAME  : g470
CABLE    : USB Cable
DRIVER   : USB UPS Driver
UPSMODE  : Stand Alone
STARTTIME: 2026-05-01 23:00:20 +0900  
MODEL    : APC ES 550M1 
STATUS   : ONLINE 
LINEV    : 105.0 Volts
LOADPCT  : 18.0 Percent
BCHARGE  : 100.0 Percent
TIMELEFT : 37.5 Minutes
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME  : 0 Seconds
SENSE    : Medium
LOTRANS  : 87.0 Volts
HITRANS  : 113.0 Volts
ALARMDEL : 30 Seconds
BATTV    : 13.7 Volts
LASTXFER : Automatic or explicit self test
NUMXFERS : 0
TONBATT  : 0 Seconds
CUMONBATT: 0 Seconds
XOFFBATT : N/A
SELFTEST : NO
STATFLAG : 0x05000008
SERIALNO : 4B2232P06646  
BATTDATE : 2023-02-26
NOMINV   : 100 Volts
NOMBATTV : 12.0 Volts
NOMPOWER : 330 Watts
FIRMWARE : 933.a7 .A USB FW:a7
END APC  : 2026-05-02 00:05:44 +0900  
```

## Prometheus监控

我让AI给我推荐了几个方案，比如 [mdlayher/apcupsd\_exporter](https://github.com/mdlayher/apcupsd_exporter) ，不过我最后选择了 [56quarters/apcmetrics ](https://github.com/56quarters/apcmetrics)。

Clone到本地，进入项目文件夹，然后直接 `make build`，注意，你得先把make和[go](https://go.dev/doc/install)装上，我很少在服务器上Build软件所以居然都没有装。

然后把Build出来的产物送到bin目录下：`sudo mv apcmetrics /usr/local/bin/`

按照教程复制Service并且启动就好了。

```
sudo cp ext/apcmetrics.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable apcmetrics.service
```

完后可以用curl确认一下是不是已经启动了：`curl http://127.0.0.1:9780/metrics`

在Prometheus里面增加监控项：

```yaml
scrape_configs:
  - job_name: apcmetrics
    static_configs:
      - targets: [ 'you-ip:9780' ]
```

重启或者Reload即可。

## Grafana Dashboard

没找到Dashboard，自己动手撸了一个 很粗糙的版本，以后慢慢优化了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-06fc7bc548a8c182af09840f031305de0f80cb16%2F2026-05-02-00-14-23.png?alt=media)

[json配置文件放在这里了](https://gist.github.com/TsingJyujing/d2830a556820a5c02494bf104a2ddbeb)，需要的自取。

## 有些麻烦但是想做的事情

* NAS断电的重启恢复会有些困难，如果能够时刻监控剩余使用时长，在断电前执行关机操作会非常好。
* 重新来电以后树莓派会自动启动 ，这个时候如果能够通过网络自动启动服务器那就更好了。

## 吐槽

我买UPS才花18000日元，[换电池](https://www.se.com/jp/ja/product/APCRBC122J/%E4%BA%A4%E6%8F%9B%E7%94%A8%E3%83%90%E3%83%83%E3%83%86%E3%83%AA%E3%83%BC%E3%82%AD%E3%83%83%E3%83%88-br400sjp-br550sjp-be550m1jp%E7%94%A8/)居然也要15000日元，这何尝不是一种计划报废呢？


# Linux 下利用 mdadm 命令设置软件 RAID

## 前言

RAID(Redundant Array of Independent Disk 独立冗余磁盘阵列)，可以用多个小硬盘组成大硬盘，也可以用来容错。 由于机械硬盘的损坏存在相当大的不确定性，而两块硬盘一起损坏的概率不高，所以我们可以用MDADM做一个软件RAID。 手上正好有两块1T的硬盘和一块3T的硬盘（不过都是移动硬盘），全部连接到主机上！

决定给两块1T设置一个软件RAID1，存放一些重要资料，3T的硬盘放一些不是很重要的资料。 其实也可以将2块1T的硬盘设置为RAID0，记作分区A，随后在3T硬盘上分出一个2T的分区，记作分区B，将A和B再组装成RAID1。这样就是2T存放重要资料的空间和1T不重要资料的空间。 不过我手上不重要的视频，电影等等比较多，动漫又全部做了蓝光备份，所以重要资料就是一些数据集和书籍，1T足矣。

这里多说一句，不是很推荐RAID5，重建成功率太低了。

## 设置方式

### 现实支持的分区特性和当前的MD设备

（注：我这里是做好分区以后运行的，所以会有一个MD0的设备）

```
$ cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
md0 : active raid1 sdd1[0] sdc1[1]
      976628736 blocks super 1.2 [2/2] [UU]
      bitmap: 0/8 pages [0KB], 65536KB chunk

unused devices: <none>
```

### 对挂好的硬盘分区并且格式化

以对设备sdc操作为例：

```
parted /dev/sdc --script 'print'
parted /dev/sdc --script 'mklabel gpt'
parted /dev/sdc --script 'mkpart primary 0% 100%'
/usr/sbin/mkfs.ext4 /dev/sdc1
```

做好以后使用`lsblk`查看一下分区情况，这里可以看到已经完美的分好了：

```
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
...
sdc       8:32   0 931.5G  0 disk
└─sdc1    8:33   0 931.5G  0 part
sdd       8:48   0 931.5G  0 disk
└─sdd1    8:49   0 931.5G  0 part
...
```

### 设置RAID

设置很简单，我们就在`sdc1`和`sdd1`上设置了，直接一行命令搞定：

```bash
sudo mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sdc1 /dev/sdd1
```

### 后续处理

首先挂载md0设备到你喜欢的文件夹。

```
mount /dev/md0 /media/md0
```

也要记得将设置写入配置并且更新信息。

```
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
sudo update-initramfs -u
```

### 查看现在的状态

再运行`lsblk`，可以看到，设备已经

```
NAME    MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
...
sdc       8:32   0 931.5G  0 disk
└─sdc1    8:33   0 931.5G  0 part
  └─md0   9:0    0 931.4G  0 raid1 /media/md0
sdd       8:48   0 931.5G  0 disk
└─sdd1    8:49   0 931.5G  0 part
  └─md0   9:0    0 931.4G  0 raid1 /media/md0
...
```

### 灾后重建

假设分区损坏了，我们需要恢复，方法记在下面，但是没有试过：

```bash
#（假设sdc1坏了）
# 设置损坏分区
mdadm /dev/md0 --set-faulty /dev/sdc1
# 移除故障分区
mdadm /dev/md0 --remove /dev/sdc1
# 重新准备一个好的SDC
# 添加分区以后应该自动回开始重建
mdadm /dev/md0 --add /dev/sdc1
# 可以通过如下命令查看重建进度
mdadm -D /dev/md0
```

## 后记

软件RAID的性能其实挺感人的，我的移动硬盘支持USB3.0，但是因为电脑旧了所以连接了USB2.0，就是这样以前单盘性能应该是40～50Mb/s左右，结果挂上RAID以后写入速度掉到了14Mb/s，稍稍有些过于感人，不过读取速度还行，但是也不能达到理论上单盘的两倍速度。

所以如果对性能有追求的去购买硬件RAID的设备比较好，或者有机箱的人可以考虑一下PCI接口的RAID卡。

从小到大我已经搞坏过好多硬盘了，所以千万记得数据备份，或者做RAID啊！


# 简易的Linux迁移指北

这篇文章写的比较简洁，如果有什么不能理解的，可以搜索一下（但是不要用百度，否则你的疑惑可能更深）。

## 不适宜使用Linux的人群

首先说说哪些人已经被绑死在了Mac/Win的船上下不去了，别的行业不了解，我说说工程师一类的：

如果你重度依赖一些无法迁移到Linux的软件系统，比如：

* 某些CAE/CAD绘图软件
* 开发Mac和iOS程序的Xcode
* Visual Studio
* Excel VBA
* 一些硬件烧写程序和开发工具（比如Keil）
* ...

那就老老实实用Mac和Win好了。 或者电脑的驱动比较封闭，无法正常运行Linux的，我建议使用WSL体验Linux也还凑合。

如果你懒得折腾，愿意呆在舒适区，那么不论你现在用的是Mac，Win，还是Linux，切换对你都意味着痛苦，对不？

## 为什么用Linux

原因其实可以有很多，就我自己的话，除了开发方便以外，我希望控制我自己买的电脑。

前段时间Apple的OCSP服务器宕机导致我什么软件都启动不了的经历对我的刺激太大，否则看在设计美观的份上我是愿意接受Mac的。 这次宕机事件让我觉得我的电脑只是苹果公司租给我的，很没有安全感，好在这台电脑是公司的，否则我真的要气死了。 Windows就更不要说了，我是Win95开始的骨灰用户，写注册表调Win32API开发都经历过，以前还挺喜欢，自从Win10开始了：“我是你爹，你必须更新”的模式以后，就再也不想用了。

## 安装Ubuntu 20遇到的坑

我个人现在用Ubuntu，但是我同样推荐Deepin，尤其是你不太愿意花时间配置一些细节，Deepin给你提供一个比较完美的开箱即用的体验。不过需要说的是，新版（20）Deepin的默认界面，我觉得是变丑了。

* 无线驱动有可能打不上
  * 其实这个和Ubuntu其实没有关系，如果Securety Boot开启的话，驱动就不一定能打的上
  * 话虽如此，Securety Boot该开还是最好要开的
* 磁盘的话，XPS上不兼容RAID模式，要选ACHI模式
* 不推荐安装系统的时候同时安装更新，完全可以安装好以后一边刷B站Youtube一边喝可乐一边更新啊！
  * 尤其是家里或者公司里搭建了Nexus的APT镜像的，安装好以后先修改源再更新，那叫一个快啊！
    * 当然前提是你家里或者公司里还有一个Linux服务器
* 老生常谈了，安装的时候最好选择英语，否则文档，下载之类的文件夹都是中文的，在命令行里不太方便。
* Ubuntu 20 的声音在有多个输出设备的时候切换的优先级很诡异

## 老生常谈的软件问题

初到Linux的时候，曾经有很多困难，比如烧写U盘的时候没了破解版的UltraISO，但是事实上Linux下根本不需要这么麻烦。比如制作Win10的启动盘，其实格式化成NTFS以后往里面拖文件就好。如果你实在想烧某个盘，可以用dd命令，如果觉得命令行有门槛，Ubuntu还有启动盘制作工具，磁盘管理工具还支持创建和还原磁盘镜像，树莓派一般是这么烧写的。

再比如，Putty和XShell没了，但是你有原生的SSH啊，你完全可以通过SSH Config文件配置跳板机，配合ZSH的自动补全，其实效率只有更高。 没有WinSFTP，但是scp速度更快，rsync灵活度更高，实在想用图形化，有FileZilla，事实上，用了Linux后，我都不用FileZilla了。

很多时候，我们沉溺在Windows下拙劣的模仿者里，回过头来看一看Linux，其实最佳实践已经给我们准备好了。

* 视频播放：不要说了，VLC永远的神，不止在Linux上，Mac和Win上我也都用。
* Office系列软件：除了自带的 Libra Office，还可以选择 WPS Linux版的，M$ Office可以用在线的，还有Google Doc，腾讯文档等等。
* GIMP：我在Mac和Win上也用，一直当成开源的PS用，不过我其实不太会PS也不太会GIMP（捂脸）。

国产软件我们单独说，其余的我真的没有见到Mac/Win上能完成Linux上完不成的任务。

## 国产软件

国产软件（这里特指国内各大互不连网公司的大作）不少是毒瘤一般的存在，喜欢弹窗，扫硬盘等等，所以我的宗旨是能不用就不用。

更大的问题是，国产软件大多只支持Windows，有的支持Mac，支持Linux的很少，所以用起来更加复杂。 虚拟机固然是个不错的解决方案，但是相比之下过于消耗资源，所以我这里介绍的方法就不谈虚拟机了，用虚拟机的话，VirtualBOX或者KVM都不错，前者比较容易上手，后者更加强大。

## 微信

微信是支持网页版的，但是腾讯官方有意的削弱网页版，实际情况是很多账号不能登录网页版。

所以我推荐两个工具：

* [electronic-wechat](https://github.com/geeeeeeeeek/electronic-wechat)：这是把网页版微信封装了一下，优点是和系统结合比较好，缺点是不是所有人都能用。
* [DoChat](https://github.com/huan/docker-wechat) 这个是用Docker版的WINE虚拟Windows环境来运行Win版的微信。优点是功能全，缺点是显示，文件和托盘图标有点小问题。

另，最近得到的消息，不是很准确，有时间的朋友帮我测试下： 为了配合国家的科技自主化战略，微信最近号称开发了原生支持Linux（目前仅仅支持UOS）的版本。但是网友说实际上是网页版微信套壳，是个和[electronic-wechat](https://github.com/geeeeeeeeek/electronic-wechat)差不多的东西，所以腾讯的策略是，只要尝试登录UOS上的微信，就为你的账号解锁网页微信的功能。

## QQ

QQ 其实是有Linux版本的，虽然界面丑爆（简直梦回2008），功能不全，但是胜在干净。缺点还有一个，每次登陆都要你手机扫码，腾讯要确保你安装了手机客户端（这样才能更好的监控你（逃））。

[Linux QQ](https://im.qq.com/linuxqq/index.html)

## QQ，百度云之类的其它软件

和WINE微信思路一致，也可以用[RokasUrbelis/docker-wine-linux](https://github.com/RokasUrbelis/docker-wine-linux)这个项目来使用WINE来跑国内的各大毒瘤软件。

## 可操作性解决方案

主要涉及鼠标特殊按键，快捷键，人脸识别，输入法等等。

### Logitech 鼠标

Logitech 鼠标（其它牌子没试过，应该差不多）有多余的特殊按键，我一般设置为切换应用等等，Linux下我参考了：[How to configure extra buttons in Logitech Mouse](https://askubuntu.com/questions/152297/how-to-configure-extra-buttons-in-logitech-mouse)

根据自己的使用习惯修改即可。

### 人脸识别

用的是[boltgolt/howdy](https://github.com/boltgolt/howdy)，实际使用体验稍显鸡肋。如果不是快速模式，则基本要正脸面对摄像头才能PASS，但是在双屏的时候有点做不到。 其次，至少快速模式可以被照片破解。最后，识别的速度并不快，（可能比我手速还慢一点）所以我虽然安装了，但是最后还是disable掉了。

### 指纹识别

DELL工程师回复的方法在这里：[XPS 13 9300 - Does fingerprint reader work on linux?](https://www.dell.com/community/XPS/XPS-13-9300-Does-fingerprint-reader-work-on-linux/td-p/7514958)，但是我的笔记本的传感器驱动还没有，所以这是我用Linux唯一的痛。

### 触摸板手势

如果问我使用Mac，体验最好的地方是什么，那应该是触摸板强大的功能。Linux下也有相应的解决方案，亲测，如果好好配置，和Mac的不相上下。

我参考了这个帖子：[Touchpad Gestures in Ubuntu 18.04 LTS](https://askubuntu.com/questions/1034624/touchpad-gestures-in-ubuntu-18-04-lts)，我在用的是 [iberianpig/fusuma](https://github.com/iberianpig/fusuma)。

安装方式：

```bash
sudo gpasswd -a $USER input
sudo apt install libinput-tools xdotool ruby
sudo gem install fusuma
```

配置文件位置是：`~/.config/fusuma/config.yml`，需要自己创建，内容参考：

```yaml
swipe:
  3: 
    left: 
      command: 'xdotool key alt+Right'
    right: 
      command: 'xdotool key alt+Left'
    up: 
      command: 'xdotool key super'
    down: 
      command: 'xdotool key super'
  4:
    left: 
      command: 'xdotool key ctrl+alt+Down'
    right: 
      command: 'xdotool key ctrl+alt+Up'
    up: 
      command: 'xdotool key ctrl+alt+Down'
    down: 
      command: 'xdotool key ctrl+alt+Up'
pinch:
  in:
    command: 'xdotool key ctrl+plus'
  out:
     command: 'xdotool key ctrl+minus'

threshold:
  swipe: 0.4
  pinch: 0.4

interval:
  swipe: 0.8
  pinch: 0.1
```

配置好后直接`sudo fusuma`就可以运行了，建议加入启动程序中，比较方便。

### 输入法

只要在语言中安装了汉语以后，就可以添加ibus智能拼音输入法。 对了，记得开启记忆词汇功能和内置的词典，否则输入还是比较痛苦的，有用户数据的可以尝试导入历史数据。

日语输入推荐用Mozc。


# 记录一次rsync命令引起的异常

在开发中，我使用 `rsync -avPz --delete ./ 目标机器:目标文件夹`将我的源代码同步到目标机器上，达到本地IDE编辑，远程服务器运行的目的。 源码不多，为了省去手动操作的麻烦，我干脆使用watch命令每两秒同步一次。

中间连接内网的VPN断开一次，之后发现，机器无法SSH登陆，提示错误：`shell request failed on channel 0`，好在管理员账户还是可以登陆，登陆以后使用`sudo su - 我的用户名`，提示：`su: failed to execute /bin/zsh: Resource temporarily unavailable`。

这就很奇怪了，我确认了zsh是存在的，半信半疑之间，我还是yum重装了zsh，并且修改`/etc/password`，让我的用户使用bash，结果还是不行。网上搜索问题也都搜不到点子上。

我的直觉告诉我，可能是连接数过多了，于是我使用who命令检查，果然有好几个会话，但是仍然很奇怪，因为平时我工作的时候，偶尔会话开的比这个多的时候也有。 于是我使用 `ps aux|grep sshd|grep 我的用户名`，好家伙，洋洋洒洒出来一大堆进程，没仔细数，于是使用：

```bash
# 注意，此命令有误杀风险，最好先grep出来看一眼再加上 |awk '{print $2}'|xargs -r sudo kill -9 。
sudo ps -ef|grep sshd|grep 我的用户名|grep -v grep|awk '{print $2}'|xargs -r sudo kill -9
```

全部干掉，恢复正常。 详细起因仍然不清楚，但是这么多ssh会话除了rsync也没谁能建立了。 回头还是使用`inotify`配合`rsync`使用吧，偷懒还是不行的。


# 网络


# 在OpenWRT路由器上配置Cloudflare DDNS

修订日志：

* 2026-04-30 初稿

## 前言

最近准备把一个数据库共享给小伙伴用。Cloudflared的性能不太够用，想来想去就看看能不能直接上公网，结果意外发现了家里是有公网IP的，甚至连80和443都是可用的。

上一次NTT光封禁了我的80和443端口以后我就一直没试（包括搬家后），但是现在用的是BBIQ的网络，形势可不一样了。

于是我准备把DDNS设置起来。

我上一次设置DDNS还是用Aliyun的API（[这是我的方案](https://github.com/TsingJyujing/aliyun-ddns-client)）

现在我把域名迁移到Google又迁移到Cloudflare，方法也准备换一下，直接在OpenWRT上配置。

## OpenWRT软件安装

需要在OpenWRT上安装以下软件：

```
ddns-scripts
ddns-scripts-cloudflare
ddns-scripts-services
luci-app-ddns
bind-host
```

不安装luci-app-ddns的话就没有图形界面配置，不安装bind-host的话其实也能用，但是会有一个警告，我忘记复制了，好像是无法通过TCP确认绑定的情况。

## Cloudflare Token准备

在Cloudflare上新建一个API Token，赋予DNS的编辑权限。

我知道还有一种是API Key，但是不安全，我也就没有尝试。

## 配置

安装好之后导航栏上应该出现Services，点击Services -> Dynamic DNS进入配置页面。

安装好的ddns-scripts默认带了两个配置，可以都删掉重新配置。

注意，创建配置（Service）的时候，Name是不能修改的，所以不要乱起名，回头还得删掉重来。

| Item                  | Example Value               | Comment                                                             |
| --------------------- | --------------------------- | ------------------------------------------------------------------- |
| Lookup Hostname       | something.your-domain.com   | 这个是用来确认你的IP是否正确更新了的，如果是泛解析，由于不支持直接解析\*，那就随便填一个xxxxx.your-domain.com |
| IP address version    | IPv4                        | IPv6还没有普及，应该大多数人都是IPv4                                              |
| DDNS Service provider | cloudflare.com-v4           | 不解释                                                                 |
| Domain                | <something@your-domain.com> | 这里需要用@把域名的前后段隔离开，如果是泛解析那么就是\*@your-domain.com                       |
| Username              | Bearer                      | 如果你是用Token的话设置这个就好                                                  |
| Password              | \*\*\*\*\*\*\*\*\*          | 刚才你生成的Token填在这里                                                     |

配置好以后Save\&Apply，然后Reload，应该就能看到DDNS设置成功了。

## 感想

路由器买小了。我用的是TP-Link Archer C7 v5路由器，内存都有120M，但是存储只有10M，随便安装点什么软件就满了。只能说TP-Link真的是太奸商了。 更要命的是，OpenWRT比原生的固件还要稳定。 以前我经常听书听到一半——网断了，半夜三更起来重启路由器。 对，我最初折腾OpenWRT不仅仅是为了功能，还是为了稳定性。 现在连续运行个把月一点问题都没有。我还配了一个方波UPS，现在断电都不带重启的。

如果路由器再大一点，那么我直接会把Nginx，更新SSL证书的东西全套放上去。


# 使用FRP进行内网穿透

我本是个有公网IP的人，奈何最近NTT把我的443端口给封禁了，我便不能再（优雅的）配合DDNS使用自带的这个动态公网IP了。

没办法，只得开了一台距离比较近的VPS，在上面设置了一个FRP服务器。在此记录一下设置的过程，以备不时之需。

## 获取FRP

FRP是一个开源的反向代理项目：<https://github.com/fatedier/frp> 可以从Release页面下载需要的二进制文件：<https://github.com/fatedier/frp/releases>

### OpenWRT

如果你的路由器够强劲，可以试试OpenWRT版本的：

* <https://openwrt.org/packages/pkgdata/frpc>
* <https://openwrt.org/packages/pkgdata/luci-app-frpc>

其中，luci-app-frpc是在路由器的配置页面上直接配置frpc，但是我试过，其实未必有直接写配置文件来的便捷。

直接：

```shell
opkg update
opkg install frpc
opkg install luci-app-frpc
```

即可。

虽然说一般不会有人要在路由器上设置FRPS，但是还是写一下：

* <https://openwrt.org/packages/pkgdata/frps>
* <https://openwrt.org/packages/pkgdata/luci-app-frps>

## 服务器配置

首先下载frp，然后解压：`tar -xzvf frp_0.52.3_linux_arm64.tar.gz`

随后将文件复制`frps`到`/usr/bin`(或者任何在PATH里的目录): `cp frp_0.52.3_linux_arm64/frps /usr/bin/`

准备以下文件：

`/etc/frp/frps.toml`

```toml
bindPort = 7000
auth.method = "token"
auth.token = "自己设置想要的密码"
# 以下配置为可选项
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "自己设置想要的管理员密码"
```

`/etc/systemd/system/frps.service`：

```
[Unit]
Description = frp server
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
ExecStart = /usr/bin/frps -c /etc/frp/frps.toml

[Install]
WantedBy = multi-user.target
```

之后

```shell
systemctl daemon-reload
systemctl enable frps
systemctl start frps
```

即可启动服务，服务一经启动，即不需要进一步的配置，其余的配置都在客户端。

还有一点需要确认的是防火墙，每个系统的防火墙不一定一样，这里是Ubuntu的服务器，所以用ufw配置。

需要开放7000端口和你之后想要对外放出的端口（这里是443）：

```
ufw allow 7000/tcp
ufw allow 443
```

## 客户端配置

同样是下载软件后复制`frpc`到`/usr/bin`(或者任何在PATH里的目录): `cp frp_0.52.3_linux_arm64/frpc /usr/bin/`

准备配置文件和服务文件：

`/etc/frp/frpc.toml`

```toml
serverAddr = "VPS的地址"
serverPort = 7000
auth.method = "token"
auth.token = "自己设置想要的密码"

# 本段落可以配置多个
[[proxies]]
name = "自己给个名字"
type = "tcp"
localIP = "目标机器的地址"
localPort = 目标机器的端口
remotePort = 想要在VPS上暴露的端口
```

`/etc/systemd/system/frpc.service`

```
[Unit]
Description=Frp Client Service
After=network.target
[Service]
Type=simple
DynamicUser=yes
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frpc -c /etc/frp/frpc.toml
ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.toml
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
```

之后启动服务即可

```shell
systemctl daemon-reload
systemctl enable frpc
systemctl start frpc
```

### 关于OpenWRT

如果显示`frp Client - NOT RUNNING`，需要到 <http://192.168.1.1/cgi-bin/luci/admin/system/startup> 里面启动（Enable\&Start）frpc服务。

以及，如果目标机器和跑FRPC的机器不是同一台(如路由器跑frpc，树莓派跑服务)情况下，建议设置固定的IP地址。


# Let's encrypt -- 让我们一起愉快的使用HTTPS

## 前言

最近几年，我们跑步进入了HTTPS的时代，得益于SSL，我们的隐私得到了非常好的保护。 HTTPS的根基是[非对称加密技术](https://www.liaoxuefeng.com/wiki/1252599548343744/1304227873816610)，这个技术可以在即使有第三方完整监听的情况下进行安全的数据传输，可谓是对抗极权，保护个人利益的神器。

当然了，最近美国有人准备[以禁止儿童色情的名义禁止点对点加密](https://www.theguardian.com/technology/2020/mar/06/us-internet-bill-seen-as-opening-shot-against-end-to-end-encryption)，这就是后话了。

除了安全以外，HTTPS还可以杜绝很多运营商的流氓行为，比如HTTP劫持和DNS劫持。

* [遇到运营商的 DNS 劫持广告应该怎么办？](https://www.zhihu.com/question/20418863)
* [为什么运营商劫持的行为会普遍存在且无人监管？](https://www.zhihu.com/question/29340133)

我以前还住在上海的时候就遇到过联通的劫持，给你的HTTP页面注入广告，都TM注入到我们公司的GIT服务器上了。

## 为什么HTTPS这么难

作为一台个人PC，不可能存下所有网站的证书，更何况证书还会更新，这就变成了一个先有鸡还是先有蛋的问题，为了解决这个问题，我们引入了一个叫“根证书”的概念。 只有根证书签发的证书才是可信的，制作一张证书本身不需要任何费用，但是：

> 审核 ，验证 CSR 成本，支持成本，法律成本(保险费用，担保费用) 要进入各个浏览器的根证书列表，WebTrust 年度审计费用，是很大的开销 一些浏览器厂商还会对植入根证书列表的 CA 收费 基础设施开销，CRL 和 OCSP 服务器成本 验证 CSR：就是提交证书申请后，CA要做多项验证，越是高级的证书（比如EV）验证越麻烦。不固定开销，有些要花费很多人力和时间来完成。 CA链费用：新开的CA公司要等5-10年，才会被普遍信任，才能广泛进入根证书链。要想加快点，就得给别的大牌CA公司掏钱，买次级证书。

所以现在市面上的HTTPS证书大约是几百到几千元不等。

## 为什么不用某些免费签发HTTPS证书

比如阿里云就提供一次管一年的免费HTTPS证书，但是这个证书不能做到泛解析，而且相比以前，现在的阿里云把免费HTTPS的申请藏的越来越深了，绞尽脑汁让你买5800一年的证书。兄弟，我一个不盈利的个人站哪来这么多钱给你坑，所以果断选择了Let's Encrypt。

## Let's Encrypt

但是HTTPS其实不需要这么麻烦，不是吗？我们只需要证明你是你就可以了，于是就有了 <https://letsencrypt.org/> ，通过某种约定（比如在网站的某个目录下放一个特定的文件，或者增加一条解析什么的）证明网站是你的，我们就可以用根证书为你签名一张HTTPS证书。

具体的原理在官网上可以看到：[Let's Encrypt 的运作方式](https://letsencrypt.org/zh-cn/how-it-works/)。

一次生成的证书管3个月，但是只要你能控制服务器，其实可以用定时任务自动续命。

首先是安装软件：

```bash
apt-get install letsencrypt -y
```

随后生成证书就可以了：

```bash
letsencrypt certonly --standalone
```

输入命令以后一步步操作，证书就有了。 在执行之前记得停止你的Nginx（或者其它）服务器，你需要空出443端口等待证书发行方的检验，整个过程非常快，可以在1分钟内完结。

## ACME协议

ACME的全称是 "Automatic Certificate Management Environment" 自动证书管理环境。详细的内容可以看这里：[RFC 8555](https://datatracker.ietf.org/doc/rfc8555/?include_text=1)

ACME协议的好处是不需要停止你的服务器，只需要加上一个“无意义”的域名解析，证明域名是你的就可以。缺点就是需要加一条解析会麻烦一点。

还有一个好处就是可以实现泛域名。

ACME的一个比较好的实现在这里：[acmesh-official/acme.sh](https://github.com/acmesh-official/acme.sh) 结合你的域名提供商，可以做到快速生成，自动续命。

首先安装ACME.SH

```bash
curl  https://get.acme.sh | sh
```

由于我是在阿里云上买的域名，所以首先在阿里云上生成Key和Secret并且赋予DNS和域名的管理权限。其它参见[官方文档](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E)

随后填写你的环境变量后就可以开始执行证书制作了。

```bash
export Ali_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
export Ali_Secret="jlsdflanljkljlfdsaklkjflsa"

acme.sh --issue --dns dns_ali -d example.com -d www.example.com
```

这个执行大约需要3～5分钟，可以去泡杯咖啡，回来以后一切就都设置好了。

随后会给你的crontab里面加上一条记录：

```
45 0 * * * "/home/[你的用户名]/.acme.sh"/acme.sh --cron --home "/home/[你的用户名]/.acme.sh" > /dev/null
```

这样就可以随时保持更新了，如果你觉得心里不舒服，可以修改或者删除。 所有的东西都放在：`/home/[你的用户名]/.acme.sh` 里面，移除也很方便。

## 自动下载并且更新泛解析域名

（这是来自2026年5月1日的更新）我现在把域名迁移到Cloudflare上了，正好[有位大神写了个脚本](https://github.com/kavehbc/free-ssl-cloudflare)，可以自动更新泛解析域名的证书。

只需要创建一个有读写DNS权限的token，设置如下的Docker Compose就好：

```yaml
services:
  wildcard-ssl:
    image: kavehbc/free-ssl-cloudflare
    container_name: wildcard-ssl
    environment:
      - CRON_INTERVAL=0 2 * * *
      - DOMAIN=your-domain.com
      - CLOUDFLARE_API_TOKEN=YOUR-TOKEN-HERE
      - SSL_EMAIL=your-mail-addr@something.com
      - AUTO_RENEW=true
    volumes:
      - ./letsencrypt:/etc/letsencrypt
    restart: unless-stopped
```

稍等片刻就可以在letsencrypt下面看到你的证书了：

```
letsencrypt
├── accounts
│   └── acme-v02.api.letsencrypt.org
│       └── directory
│           └── 3f8ed3d7ca389e73c1d931a9e058fa82
│               ├── meta.json
│               ├── private_key.json
│               └── regr.json
├── archive
│   └── tsingjyujing.com
│       ├── cert1.pem
│       ├── chain1.pem
│       ├── fullchain1.pem
│       └── privkey1.pem
├── cloudflare.ini
├── live
│   ├── README
│   └── tsingjyujing.com
│       ├── cert.pem -> ../../archive/tsingjyujing.com/cert1.pem
│       ├── chain.pem -> ../../archive/tsingjyujing.com/chain1.pem
│       ├── fullchain.pem -> ../../archive/tsingjyujing.com/fullchain1.pem
│       ├── privkey.pem -> ../../archive/tsingjyujing.com/privkey1.pem
│       └── README
├── renewal
│   └── tsingjyujing.com.conf
└── renewal-hooks
    ├── deploy
    ├── post
    └── pre
```

## Nginx 配置

有了证书以后，我们可以给HTTP升级了！

```
server { # 自动跳转到HTTPS
    listen      80;
    server_name [你的域名];
    return      302 https://$server_name$request_uri;
}

server {

    # Setup HTTPS certificates
    listen       443 ssl;
    server_name  [你的域名];
    ssl_certificate /home/[你的用户名]/.acme.sh/[你的域名]/fullchain.cer;
    ssl_certificate_key /home/[你的用户名]/.acme.sh/[你的域名]/[你的域名].key;
    ssl_session_timeout  5m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers   on;

    location / {
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Host  $http_host;
        proxy_set_header Host              $http_host;
        proxy_max_temp_file_size           0;
        proxy_pass                         http://[你的upstream];
        proxy_redirect                     http:// https://;
    }
}
}
```

注意`ssl_certificate`要用`fullchain.cer`，我之前踩坑了，网站在浏览器里是正常的，但是如果用Python访问就会显示SSL错误，原因就没有提供完整的证书。

## 总结

总之，用 Let's Encrypt 生成证书，首先你要能控制自己的服务器，其次开头的配置会有一点复杂，但是配置完以后，就可以有永久免费的证书了。

网上还有一些方案使用certbot或者其它工具，本质上大同小异。但是如果你是成立了一家公司，就不要省这些小钱了。


# 科学的公司内网连接技术选型

## 简介

这篇文章主要介绍如何在家、或者在外出差的时候能够连回办公室的内网办公，享受福报。 本文是一个简介性质的文章，具体技术细节网上有很多博客，况且最近还兴起了用Docker搭建的风潮，不能更方便了。 请不要把本文的技术用于其他用途哦，否则后果自负～（手动狗头）

本文中的技术我们主要评价两个方面：

1. 第一个是方便易用，不能为了连接一个内网弄的很麻烦，这会降低享受福报的效率。
2. 第二个是安全隐蔽，假设有坏人在你公司的外面拦截了你的通信，进行中间人攻击（比如，据我所知各大运营商都会对HTTP协议做一定的中间人攻击来注入广告），时不时还根据且听的流量给你断断网或者做重放攻击什么的。我们的连接方式要经得起考验，安全可靠而且隐蔽。

（按说没有那么多狗黑客的，可有的时候就是瘸子的屁股——斜了门了……不知道咋回事，这连接就不能稳定，所以第二项也是非常重要的考量因素。）

## 内网连接方法介绍

首先假设你在公司有一台Linux服务器，可以搭建各种服务。 这里的Linux也可以是Docker或者WSL系统，但是要设置好宿主机的网络设置。

### SSH 隧道

这个是最常用的临时方案，SSH隧道建立方便，可以反向代理映射固定的端口，也可以建立socks代理进行访问。

这里姑且假设你在公司有一台Linux服务器，并且你能以SSH登陆（一般是22端口），那这个时候你可以开通一个隧道，只需要在本机执行命令： `ssh -qTNf -D 127.0.0.1:1080 <User name>@<Public IP>`

这样就构建了一个端口是1080的socks代理。 参数就不作过多的说明了，网上有很多博客有详细的解说。

如果你在用浏览器，可以尝试安装插件：[SwitchyOmega](https://github.com/FelisCatus/SwitchyOmega)，一般火狐或者Chrome都可以下载。 在配置中使用这个端口连接，就可以达到连接公司内网的目的，这个时候你就可以打开只有在公司内网才能打开的网页。

同时，SSH还可以作为跳板机连接其他内网机器，一般在ssh config里面设置 `ProxyCommand ssh -o Compression=no <跳板机> nc %h %p` 来做，这里暂时按下不表。

安全性上，SSH采用非对称加密，黑客无法获取通信内容。 缺点是流量特征明显，容易被人刺探到这是个SSH端口（然后经常会收到弱口令攻击），所以密码一定要设置复杂一些。 同时，推荐使用公私钥登陆，而不是密码。

### HTTPS 代理

HTTP代理很容易实现，也很容易使用，历史悠久，这里就不赘述了。 但是HTTP代理也存在一个问题：流量透明，别人很轻松就知道你在搞代理，还能知道你在干什么。

所以我们加一层TLS，比如利用Nginx，就可以把一个HTTP的服务转化为HTTPS服务，甚至可以挂在到某个网站的特定的路径下。 如果公司有HTTPS的官网或者博客的话，可以尝试使用这个方法挂载HTTPS代理，记得加上Authentication。这一套都是Nginx反向代理的标准操作了。

并不推荐根路径直接连接作为代理的路径，容易被黑客刺探到这个域名就是做代理的，然后被攻击。

同样，HTTPS代理也可以被SwitchyOmega使用。

### PPTP/L2TP/OpenVPN/AnyConnect

这是非常标准的VPN，设计之初也就是为了安全的连接公司内网而设计的，Windows/Linux也有比较好的系统级别的支援。其中L2TP使用的UDP端口，使用需注意。

唯一的问题在于，如果你的公司或者子公司在海外，因为某种原因（大概是离得太远没信号吧，嗯，一定是），你的PPTP/L2TP/OpenVPN服务会非常不稳定，可以说基本不可用。

如果你有海外公司，但是又一定要享受便利，推荐你配合内网穿透一起使用。

这里的例外是AnyConnect，虽然大家（含SSH）流量特征都很明显，但是由于AnyConnect被大公司使用，海外连接的时候不易出现PPTP那样的不稳定现象。

安全性上基本都没有问题（PPTP除外），只是容易被且听到你在使用的技术。

### 其他方式

1. [v2ray](https://www.v2ray.com/)

理论上说V-Project只是一套协议规范，核心是[V-Mess协议](https://www.v2ray.com/developer/protocols/vmess.html)。 但是v2ray作为代理工具的确是功能强大，不仅仅是单纯的代理，甚至可以做到规则路由。可以创建诸如A网站使用X代理，B网站不使用，C网站使用Y代理等等复杂规则。 配置过程略痛苦，JSON文件编辑很头疼，经常一写就很长，但是配置好以后使用非常方便。是我强力推荐的工具。 V2Ray在安全上是我见过做得最好的，支持混淆，防止刺探，防止重放攻击。某些黑客基本就是**狗**拿刺猬——无从下口了。

1. Shadowsocks

Shadowsocks也只是一个协议，有很多的实现。 不过作者喝茶了，加上v2ray也支持ss协议，这里不赘述了。 这里对作者说一句Respect，感谢每一个无私开源的人。

需要说一句的是，ShadowsocksR很火，但是并不推荐使用。

1. GoProxy

GoProxy可以设置一些基础的代理，但是这个工具更大的作用是创建内网穿透，下文会介绍。

## 内网穿透

大多数的时候，你可能没有公网IP，或者你没有权限使用公网IP，或者因为某种原因你连接不上这个IP（比如你所在的地区屏蔽了这个IP）。

这个时候就需要内网穿透了，说到内网穿透，比较有名的是花生壳。但是花生壳的收费也并不便宜。 当然你也可以自建，自建内网穿透的前提是：

> 你有一台服务器，并且这台服务器有公网IP。

最简单的方式就是买了，比如百度腾讯阿里美团各种云，缺点是价格贵，公网带宽价格**异常的贵**，还不如用花生壳。

最好的方法是，尝试在办宽带的时候要求有公网IP（可能会多收钱），如果是静态的IP那就可以直接用IP，如果是动态的IP，那就买个域名做个DDNS。这是我写的阿里云的DDNS方案：[TsingJyujing/aliyun-ddns-client](https://github.com/TsingJyujing/aliyun-ddns-client)，欢迎Fork。

和之前一样，这里的服务器可以用个人电脑代替（可以在家里建立机房，想想就兴奋）。

有了服务器，内网穿透的方案大致有（花生壳以外）：

* Ngrok
* GoProxy

Ngrok的教程网上一搜一大把，GoProxy的资料少一点，参考[官方文档中内网穿透一章](https://snail.gitee.io/proxy/manual/zh/#/?id=_4%e5%86%85%e7%bd%91%e7%a9%bf%e9%80%8f)。

这里我需要评价GoProxy几句：

1. 这是个开源软件，但是源代码并不是及时更新的，总是晚于二进制包慢一点更新，而且似乎也并不全，所以并不是真正开源。
2. 曾经在7.8版本的时候我遇到过：
   * GoProxy要自动检查更新
   * 但是GoProxy的更新网址设置了反爬虫的CDN
   * 这个反爬虫CDN会导致自动更新失败
   * 自动更新失败导致整个程序无法使用

我认为这个是很好的工具，堪称代理界瑞士军刀，不输netcat，但是软件稳定性及开源精神方面有待加强。

穿透内网以后，就可以使用上面介绍的内网连接方法了，只要把端口映射出来就好。

内网穿透还有一个好处，比如你直接搭建PPTP服务器，如果你的公司或者子公司在境外的话，服务会非常不稳定，可以说基本不可用，但是如果是通过内网穿透来做，稳定性就会好很多。

## 结尾

以上就是连接公司内网享受福报的全部方法了，难免有错漏之处，也可能有些新技术没有来得及更新。 不过有机会的话，还请动手尝试一下。


# 拯救变砖的OpenWRT路由器

我的路由器是[TP-Link Archer C7 V5](https://www.tp-link.com/us/home-networking/wifi-router/archer-c7/v5/)，之后刷了[OpenWRT](https://openwrt.org/toh/tp-link/archer_c7)系统。

其实之所以要用OpenWRT，还是因为官方的固件不稳定（没错，不如OpenWRT稳定），每周左右就要“死”上一次，无法联网。重启还不能解决问题，非得恢复并且重新配置不可。这一点让我多少后悔买TP-Link的路由器。

而且OpenWRT的本身是Linux，可玩性更好，这样就可以更好的配置我的网络环境了。

今早起床，网络猛然不能用了，于是我切换到了一个备用的路由器，一遍琢磨怎么修复。

其实这个时候我应该做的是：

* 逐个断开有线的连接；
* 同时重启PPPoE并检查网络。

但是我不知道怎么脑子抽抽了，居然删掉了`br-lan`，这下作大死直接无法连接到路由器了。

## 恢复出厂设置

[官方文档在这里](https://openwrt.org/docs/guide-user/troubleshooting/failsafe_and_factory_reset)。

首先找一台电脑，随便接入一个有线的LAN口。在电脑端设置有线连接的静态IP为 192.168.1.2 (2～254都行，你挑个吉利的数字吧)，子网掩码（subnet mask）设置为`255.255.255.0`，网关（gateway）设置为`255.255.255.1`。 设置完成后给路由器断电，随后找一根牙签，顶着Reset按钮不要放，同时给路由器上电。上电以后仍然不要放手，一直等到电源灯开始狂闪烁的时候再松手。

也可以电脑时刻`ping 192.168.1.1`，看到ping通了再撒手。

这个时候一些教程提示`telnet 192.168.1.1`，但是如果你的路由器版本够新的话是需要用ssh的，只有15.05及以前需要telnet：

> Note that modern OpenWrt always uses SSH, but early OpenWrt releases (15.05 and before) offered a telnet connection in this state but no SSH.

直接`ssh root@192.168.1.1`，进入以后执行`firstboot && reboot now`即可重置所有的配置。

## 注意

路由器的整体状态会受所连接设备的影响（按照道理这不应该出现，但是急着上班没时间看了），所以在恢复的时候尽量保证只连接必要的设备，甚至可以设置好PPPOE以后再连接WAN口网线并Restart连接。

操作路由器的时候一定要冷静，记得Review自己的修改再Apply，避免造成生产事故。

这次影响我网络的设备是一个RaspberryPi 4，平时就是个小透明，但是不知道这次怎么了，居然能搞断我的网络。


# V2RAY使用方法备忘

v2ray或者v2fly是有官方文档的，但是官方文档时不时有所错漏之处，我经过阅读他人写的文档加上自己测试，总结为这篇文章，以备我后续查用。 其路由的功能非常好用，有了路由功能就不需要经常切换网络，甚至可以直接远程配置家里的路由器。

这个文档里，配置文件的设计原则是：

1. 尽可能保证联通性，保证该访问的到的页面都要访问的到
2. 在保证1的基础上，尽可能的直接连接
3. 做好保密和伪装

## 日志模块

在Docker里面运行的时候，我习惯把日志吐到`/logs`，然后再把某个文件夹挂载到`/logs`里面。

```json
"log": {
    "loglevel": "warning",
    "access": "/logs/access.log",
    "error": "/logs/error.log"
},
```

缺点是时间一长容易忘记清理，而且也不能用在其它客户端的配置里。

所以保险起见的话：

```json
"log": {
    "loglevel": "warning"
},
```

## DNS

```json
"dns": {
    "servers": [
        "114.114.114.114",
        {
            "address": "1.1.1.1",
            "port": 53,
            "domains": [
                "geosite:geolocation-!cn"
            ]
        }
    ]
},
```

## 入口配置

入口配置会在本地打开端口，一般常用的入口配置如下：

### Socket代理

只建议在本地或者局域网内使用，不加密。

```json
{
    "listen": "127.0.0.1",
    "port": 10808,
    "protocol": "socks",
    "settings": {
        "auth": "noauth",
        "udp": true,
        "userLevel": 8
    },
    "sniffing": {
        "destOverride": [
            "http",
            "tls"
        ],
        "enabled": true
    },
    "tag": "socksxxx"
},
```

### HTTP代理

只建议在本地或者局域网内使用，不加密。

```json
{
    "listen": "127.0.0.1",
    "port": 10809,
    "protocol": "http",
    "settings": {
        "userLevel": 8
    },
    "tag": "httpxxx"
}
```

### VMESS协议代理

这里的VMESS Inbound有两种用途，第一种是单独用作服务器和客户端的传输渠道（这个时候加上WebSocket更好），第二种是在本地局域网搭建客户端以后利用手机上的App访问局域网内机器，这个时候只是借用一下这个协议（配置方便）。

```json
{
    "port": 3238,
    "protocol": "vmess",
    "settings": {
        "clients": [
            {
                "id": "a83baffa-2c05-4bd9-8d86-10c7e278997a",
                "security": "auto",
                "level": 0
            }
        ]
    },
    "tag": "v2ray"
}
```

#### 客户端配置

```json
{
    "protocol": "vmess",
    "settings": {
        "vnext": [
            {
                "address": "域名或者IP",
                "port": 12345,
                "users": [
                    {
                        "id": "a83baffa-2c05-4bd9-8d86-10c7e278997a",
                        "security": "auto",
                        "level": 0
                    }
                ]
            }
        ]
    },
    "tag": "aaa"
}
```

#### WebSocket

```json
{
    "port": 12345,
    "protocol": "vmess",
    "settings": {
        "clients": [
            {
                "id": "a83baffa-2c05-4bd9-8d86-10c7e278997a",
                "security": "auto",
                "level": 0
            }
        ]
    },
    "streamSettings": {
        "network": "ws",
        "wsSettings": {
            "path": "/data" // Need to match the config in nginx
        }
    }
},
```

**Nginx配置**

```nginxconf
server {
    listen      80;
    server_name vr.xxxx.com; # 需要配置
    return      302 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    ssl_certificate /xxxxx/fullchain.cer; # 需要配置
    ssl_certificate_key //xxxxx/xxxx.key; # 需要配置
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    root /path/to/a/static/website; # 需要配置
    server_name vr.xxxx.com; # 需要配置
    location /data { # 需要配置
        proxy_redirect off;
        proxy_pass http://v2ray-server-local:12345; # 需要配置
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
    }
}
```

建好服务器以后，将需要配置的地方改一下，主要是：

1. 域名和相应的证书
2. 一个用于伪装的静态网站
3. websocket的地址（和上面保持一致）
4. 本地的v2ray服务器的地址和端口

有一个小思考，考虑到已经使用了标准的HTTPS进行加密，其实内层的协议未必需要是vmess，甚至可以是socket。但是不知会不会暴露流量特征。

**客户端配置**

```json
{
    "protocol": "vmess",
    "settings": {
        "vnext": [
            {
                "address": "域名或者IP",
                "port": 12345,
                "users": [
                    {
                        "id": "a83baffa-2c05-4bd9-8d86-10c7e278997a",
                        "security": "auto",
                        "level": 0
                    }
                ]
            }
        ]
    },
    "streamSettings": {
        "network": "ws",
        "security": "tls",
        "wsSettings": {
            "path": "/data"
        }
    },
    "tag": "aaa"
}
```

### 路由设置

服务端比较简单，一般直连就可以，不需要任何路由配置：

```json
{
    "protocol": "freedom",
    "settings": {},
    "tag": "direct"
},
```

客户端出口的配置如下：

```json
"routing": {
    "domainStrategy": "IPIfNonMatch",
    "rules": [
        {//连接家里局域网的，走代理，可删除
            "type": "field",
            "ip": [
                "192.168.1.0/24"
            ],
            "outboundTag": "aaa"
        },
        {//直连中国域名
            "type": "field",
            "outboundTag": "direct",
            "domain": [
                "geosite:cn"
            ]
        },
        {//其它的中国IP和私有IP都直连
            "type": "field",
            "outboundTag": "direct",
            "ip": [
                "geoip:cn",
                "geoip:private"
            ]
        },
        // TODO 可以增加更多配置
        {//直接连接到世界的互联网
            "type": "field",
            // "balancerTag": "blancerxxx",
            "outboundTag": "aaa",
            "network": "tcp,udp"
        }
    ],
    "balancers": [//可配置负载均衡
        {
            "tag": "blancerxxx",
            "selector": [
                "aaa",
                "bbb"
            ],
            "strategy": {
                "type": "random"
            }
        }
    ]
}
```


# Web开发相关技术


# JWT简介

## JWT是什么

JWT的全称是JSON Web Tokens，简单来说是一种令牌的格式，其令牌的详细内容是编码后的JSON且常用在Web领域的身份校验故称之为JWT。

一般来说，用户先用自己的用户名和密码（OAuth之类的第三方认证我们先暂时略去）送至服务器，服务器给用户发行一个Token。这个Token中包含了必要的信息，如签发人，主题（一般是用户ID），有效时间等等。 以后服务器便不再需要用户名密码，单靠这个Token就可以放心的确认用户的身份。

## JWT解决了什么问题

很多人可能会问， 既然用户已经有了用户名和密码，何必还要多此一举？先生成这个Token再使用，而不是直接使用用户名和密码呢？ 就好像[HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)那样，简单粗暴，靠HTTPS也能保证基本的安全。

主要有两个原因，第一个是安全，第二个是压力。

安全比较好理解，与经常把明文的用户名和密码扔在流量里飞想必，可以带有失效机制的JWT显然更加安全一点。第二个原因我觉得更为主要，那就是压力。我们不妨先考虑一下用户名和密码的鉴权过程是什么样的？

1. 用户发出请求，并且在HTTP请求中带上Base64编码后的用户名和密码。
2. 服务器解码请求，将用户名、密码同数据库中的内容（一般密码部分仅仅是Hash）作比对，返回通过或者不通过。

如果每秒钟只有几个请求，这样的设计当然没有问题，可如果这是一个每秒请求量数万的服务，仅做鉴权校验就会让数据库的压力非常的大。 而且目前相对成熟的数据库往往是单点的（涉及到事务的东西，即使能扩容也要脱一层皮）。

那有没有可能，我们发行一个令牌给用户，以后不要说是本后端服务器的其他副本（Replica）就算是跨服务器，也能进行验证，而且验证的过程不需要数据库？ 这样的话，我们的服务器就真正的变成了无状态了，熟悉分布式系统的小伙伴都知道，无状态可太省事儿了。

## JWT的安全性是如何保障的

既然就是Base64编码过的JSON，那么用户自己捏一个骗服务器这就是Token可以吗？

当然——不可以，JWT会进行数字签名，JWT的最后一部分会使用服务器才有的密钥和前两部分的内容一起生成一个数字签名，由于这个密钥只有服务器才有，而且通过内容也很难（此处指几乎不可能）反推出密钥，所以攻击者无法伪造JWT。

## JWT的结构详解

JWT的结构分为三块：

* 头部 Header
* 载荷 Payload
* 签名 Signature

三个部分都是Base64的字符串，用`.`链接起来，一个典型的JWT如下所示：

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ
```

其中头部解码后为：

```json
{
  "alg": "HS256",
  "typ": "JWT"
}
```

载荷解码后为：

```json
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
```

最后一个部分是二进制，用于校验前两部分之和是否有效。

## 如何使用JWT？

### HTTP Header

理论上，只要你把JWT传输给了服务器，这个事儿就算完了。我看RFC7519也没有规定到底一定要在哪里用JWT。 但是考虑到这是个Token，所以一般的使用方式是将其放在Request Header里：

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.cThIIoDvwdueQB468K5xDc5633seEFoqwxjF_xSJyQQ
```

### 以FastAPI为例

我这里用的是python-jose，直接`pip install "python-jose[cryptography]"`即可。也可以使用别的库，看这里就行：<https://jwt.io/libraries>

出于简洁考虑，我们这里略去FastAPI的框架，只展示核心代码。

首先我们要创建一个Endpoint，来发行用户的Token：

```python
...
from fastapi.security import HTTPBasic, HTTPBasicCredentials

http_basic = HTTPBasic()

def create_jwt_access_token(
    data: Dict[str, Any],
    expires_delta: timedelta,
) -> str:
    """
    Create JWT token from data
    :param data:
    :param expires_delta:
    :return:
    """
    to_encode = data.copy()
    to_encode.update({"exp": datetime.utcnow() + expires_delta})
    return jwt.encode(
        to_encode,
        'jwt_secret', # 需要自行设置，
        algorithm="HS256",# 可以换成别的支持的算法
    )

@app.post("/auth/issue-new-token")
def issue_new_token(
    request: Request,
    db: Session = Depends(get_db), # 根据你的数据库使用情况变化，此处仅供参考
    credentials: HTTPBasicCredentials = Depends(http_basic),
):
    username = basic_authentication(db, credentials) # TODO 这个函数用于同数据库交互，确认用户名是否有效，无效则抛出403异常
    access_token = create_jwt_access_token(
        data={"sub": username}, # 这里放你想要的payload
        expires_delta=timedelta(seconds=1234),# 这里放你要的过期时间
    )
    return {"access_token": access_token, "token_type": "bearer"} # 返回Token
...
```

这样就是一个极简版的JWT生成接口了，需要注意的是，这里既没有RateLimit，也没有针对错误进行处理，更是省略了与数据库交互验证用户名密码的逻辑，还是一个比较粗糙的鉴权校验函数。

那么有了鉴权校验，我们怎么在别的API里面验证JWT呢？

首先提供一个函数：

```python
...

from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

http_bearer = HTTPBearer()

def jwt_authentication(
    credentials: HTTPAuthorizationCredentials = Depends(http_bearer),
):
    """
    Verify JWT and return username if it's a valid token
    :param credentials: Authentication token in headers parsed by HTTPBearer
    :return:
    """
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        if credentials.scheme.lower() != "bearer":
            raise credentials_exception
        # 这里也可以做更多的处理和校验
        return jwt.decode(
            credentials.credentials,
            'jwt_secret', # 需要自行设置，
            algorithms=["HS256"],# 可以换成别的支持的算法
        )
    except JWTError:
        raise credentials_exception
...
```

随后在新建接口的时候，将它加入Dependencies中去：

```python
@app.get("/some/api/requires/auth",dependencies=[Depends(jwt_authentication)])
def some_api_requires_auth():
    ...
```

也可以获取其payload作进一步的处理：

```python
@app.get("/some/api/requires/auth")
def some_api_requires_auth(jwt_payload:dict=Depends(jwt_authentication)):
    # 处理 jwt_payload
    ...
```

### 以React为例

（警告：本人是二把刀的前端，蒙古大夫级别的水平，请谨慎服用本段落）

（很抱歉，由于源代码与公司业务纠缠太深，难以拿出来展示，这里仅提供一些思路，我以后争取补上源代码）

由于很多组件都会依赖后端的API，且这些API往往需要JWT，所以一般来说，我们会用Context来管理JWT，以便于跨层传输到其他组件。

在制作`AuthStateContext.Provider`的时候，我这里的思路是：

* 由于用了OAuth，JWT会从后段通过query string传入，这个时候首先检查query string有没有新的JWT Token
  * 如果有，那就提取出来，存到localStorage里面再做计较
  * 如果没有进入下一步
* 检查localStorage里面有没有存入的Token
  * 如果有，检查是否有效
  * 如果没有，或者上一步的结果为无效，则进入下一步
* 新增一个who am I这样的接口，并且用后端接口校验JWT（这一步可选，但是可以增加可靠性）
* 如果所有的校验都通过将JWT当前的状态用Provider传输给其他组件
* 否则跳转到相应的页面提示用户

对于其他组件，用`useContext(AuthStateContext)`即可获取确认有效的Token，一般来说会再包一层函数以便于使用。

## JWT的几个缺陷

JWT还是有几个小缺陷的，在设计系统的时候需要考虑，对于某些系统来说，这些缺陷可能导致JWT不能适用或者需要注意使用方式。

1. JWT一旦签发就难以撤回，这是无状态带来的代价。
2. 考虑到1，往往需要设置有效期，则客户端（或者说前端）需要维护JWT的状态。
3. JWT中的信息是默认不加密的，任何人都可以读取。
4. JWT这样的鉴权方式推荐HTTPS。

## 参考资料

* <https://jwt.io/>
  * <https://datatracker.ietf.org/doc/html/rfc7519>
* <https://en.wikipedia.org/wiki/JSON\\_Web\\_Token>
* <https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication>


# 随园食单


# 红豆沙馅

* 红小豆：500g
* 水：1250g

以上两样高压锅上汽后25分钟煮软。随后放到不粘锅中进行搅拌。

* 黄油：55g（可以再多一些）
* 砂糖：
  * 80g（微微有一些甜味）
  * 120g（作为豆包的甜味下限）
* 盐：1～2g


# 牛肉蔬菜汤

参考了意大利和西班牙红烩牛肉的菜谱和法式牛肉高汤的部分制作步骤。

## 原材料

### 主料

* 牛肉：1.5kg
* 洋葱：400g
* 西芹菜：一根，大约100g
* 胡萝卜：400g
* 西红柿：800g（可以配合新鲜西红柿和西红柿罐头）

\*以上的量都可以调整

### 辅料

* 大蒜（大约1头）
* 红葡萄酒（200ml\~400ml）
* 黑胡椒
* 罗勒
* 牛至
* 番茄酱 80g（推荐亨氏的）
* \*可以加番茄膏增加浓度
* 可以加红椒粉（不辣）增加颜色

## 制作方法

1. 牛肉切成1～2口大小焯水，洗去血沫待用
2. 洋葱切成丝，大蒜切成末，放入锅中，加一些黑胡椒炒到褐色，放入汤锅中
3. 不要洗锅，加油煎牛肉，煎出焦化层，放入汤锅中
4. 加入红酒洗锅，洗下锅上的焦化层，煮沸后煮1～2分钟
5. 番茄切成丁，胡萝卜切3\~5厘米的块，芹菜切成10cm左右的段，放入汤锅中
6. 加入适量的水，恰好淹没材料即可
7. 煮开，搅拌均匀，撇去浮沫
8. 加入番茄酱，盐，罗勒，牛至和足量的黑胡椒（以及番茄膏等）
9. 如果有高压锅，上汽后计时20分钟自然冷却泄压，如果没有，最小火炖煮2小时左右

## 注意点

* 油：推荐用橄榄油，也可以使用其它植物油或者黄油清代替
* 洋葱和牛肉的焦化操作也可以用烤箱完成，西红柿也可以整个放进烤盘中烤后去皮
* 牛肉选择稍微带一点肥肉的比较香，但是也容易腻


# 韭菜鸡蛋饼

## 制作方法

韭菜250g（参考值）切成丁（即5mm左右的小段），锅中放入少许油，将韭菜炒软炒绿后离火冷却。 鸡蛋2~~3枚（视锅和灶台大小调整，锅小可以只作两枚甚至一枚），打散后加入3~~5g盐，少许胡椒粉和其他想要的调味料（比如芝麻油）。 鸡蛋和调味料搅拌均匀以后加入炒好的韭菜，搅拌均匀以后重新加油起锅。

鸡蛋下入锅中以后，开小火并且转动锅身使鸡蛋饼薄且成熟均匀，翻面的时候如果整个翻面有困难，可以用锅铲划成小块以后逐个翻面。

我喜欢在成熟的蛋饼上洒上现磨的胡椒增加香气，你也可以根据自己的喜好调整。

## 后记

这是一个简单的菜谱，难度在蛋饼的翻面和火候上，这部分难以写成文字，只能靠练习去掌握。

唯一的要点是韭菜一定要先炒过，这样做可以提升口感，也可以在同一个鸡蛋饼里多放入一些韭菜（韭菜数量写参考值的原因就是，这道菜应该尽你的能力，在能成饼的前提下多放一些韭菜）。

我爷爷为了让不爱吃蔬菜的我尽可能多吃一些蔬菜，经常给我做这道韭菜鸡蛋饼吃。

现在把这道菜写出来，也是写给所有不爱吃蔬菜的孩子和担心的家长。


# 香草与香料

烹饪的时候会使用许多带有香味的植物，比如葱、姜、蒜、八角、桂皮、香叶、草果、肉桂、罗勒、胡椒、薄荷之类。其使用的方法也是多种多样，可以直接蒸、煮、煎、炸，也可以磨成粉撒在食物上等等。

但是大致说来，香辛料的使用大致分为两种：作为香草与作为香料。

其中香料一般都会和食材一起进行长时间的烹煮，比如法式高汤里用大葱叶卷起来的一堆香草料以及和烤制的牛骨一起炖煮的蔬菜（芹菜，胡萝卜等）都算是广义上的香料。烧红烧肉或者卤肉的时候放的生姜八角桂皮香叶等也算是香料，这些香料大多有木质的感觉但是又有所区别，如八角是浓郁的甜香味，桂皮则是较为沉稳的木香味（辞穷）。还有一些在食材腌制的时候就需要参与，磨成粉抹在食材的表面等等。无论形式如何，其共同点是通过长时间的烹煮出味，而这些味道又不易挥发（不如说易挥发的已经在烹饪中消失了），食用的时候入口之前难以感受到他们的存在，在口中停留片刻他们的香味才会附和着食材的香气充斥口腔和鼻腔。

而香草往往是在烹饪的最后放入的香味物质，比如出锅前的小葱，三杯鸡里的九层塔，出锅后撒上的一些黑胡椒碎，炸物上撒上的柠檬汁或者柠檬皮屑。其特点是香气强劲、轻盈且清新，之所以最后放，是因为这些香味过于脆弱，稍有加热或者时间一长就会挥发殆尽，这就是为什么有的时候酒店打包带回家的菜或者放到第二天吃的菜会感觉平淡，香气不复存在甚至产生一些不愉悦的气味。

当然也并不仅仅局限于以上两者，比如三杯鸡里的九层塔其实需要翻炒一下去掉过于浓烈的气味，淮扬菜的奶汤鮰鱼最后放的一点白胡椒，川菜里有泼热油的手法激发青花椒、大蒜、辣椒的香气，粤菜的清蒸鱼最后也要往葱丝上来一勺热油，就不完全符合上述香草的定义。少许的加热有的时候会产生比直接投入更好的效果。

香料和香草的同时存在，让食物的味觉有了层次，一块红烧肉，刚入口甚至尚未入口的时候是刺激的葱香（当然也可以撒一点黑胡椒），随后是焦糖、八角、桂皮等伴着动物油脂的香气，最后能细细品到酒香以及腐乳发酵的风味。这样的香气组合，伴随着甜入口，浓香咸鲜味收口的味觉体验，就显得非常的立体，嘴里的味道有了变化，就能持续带来刺激，就不会腻。 同理，做焖饭的时候，最后切一些细细的葱丝或者洋葱丝拌入其中，就不容易出现吃两口就腻了的情况。

同一种植物可以随着放入时间的不同，也可以体现香料和香草两种特性，例如爆锅和起锅的时候都放一点葱，前者是香料，后者是香草。是香料还是香草取决于你使用植物香味中的易挥发部分还是难挥发部分。当然这里我们忽略了一种特例：腌制。一般来说，香草的香味物质易挥发所以不适合长时间炖煮，所以一般都是作为外挂味使用的。而腌制是一种可以使小分子易挥发的香味物质也可以深入食材内部的技术（只不过在我的实践中，大部分情况下的香草香味仍然会随着烹饪而消失最后只留下少部分香气）。

除此之外，抛开香草和香料的定义，更加广义的来讨论这个香味植物与加热时机的问题。烹饪中经常使用分阶段加热/投放的手法来获得更加丰富立体的味/嗅觉体验，比如蒸生蚝时候使用的金银蒜碎，一部分是油炸的沉稳香气，一部分是生蒜的清新暴烈，就比单独使用一种蒜来的更合适。这里虽然都是作为香料使用，但是加热的步骤不同让其发挥不同的香气。

甚至除了加热时间，不同的溶剂也会带来不同的香料味觉体验。用水煮的葱姜大料和使用葱姜香菜炸的香料油的气味肯定是不同的。一般来说，香气更容易富集在脂肪里，所以如果有红烧肉的油，可以试试收集来炒菜，往往会获得比红烧肉汤（水的部分）本身更浓郁的香料味（注：当然，这样做你就不能把汁收的太浓，更像是东北坛肉的做法了）。

虽然说了许多关于香草和香料的东西，但是其实不必拘泥于这些条条框框。只需要在设计烹饪菜肴的时候，注意使用不同的加热时间、加热介质或者腌制技巧来获取不同的香气，使菜肴的香气层次丰富较为立体，那么加入的东西属于香料还是香草其实并不重要了。


# 皮塔饼

皮塔饼（英语：Pita），又称阿拉伯薄面包或口袋饼，是一种圆形口袋状面食，广泛流行于希腊、土耳其、巴尔干半岛、地中海东部地区和阿拉伯半岛。皮塔饼中的“口袋”是由蒸汽膨胀形成，面饼冷却后变得平坦，中间留下一个口袋。——Wiki

## 配方

| 配料     | 剂量        | 备注                        |
| ------ | --------- | ------------------------- |
| 面粉     | 250g      | 中筋面粉或者高筋面粉均可，推荐高筋面粉       |
| 油      | 20g       | 有教程推荐使用橄榄油，实际过程中任何喜欢的油都可以 |
| 盐      | 5g        | （经测试，实际操作中略少一些也可以）        |
| 糖      | 5g        | -                         |
| 泡打粉    | 1g        | -                         |
| 酵母粉    | 2g        | -                         |
| 水      | 115g～125g | -                         |
| 鸡蛋     | 1个        | 如果不放鸡蛋，则一个鸡蛋约折合50g水       |
| （可选）奶粉 | 10g       | 这里我没有奶粉，所以将一部分水换成牛奶       |

## 面团的制作和发酵

### 大面团制作

如果有厨师机或者面包机的话，可将上述材料直接混合到一起搅拌，然后发酵就行。

如果没有，那就采取以下步骤：

1. 将酵母粉用水化开备用
2. 将面粉，盐，糖，泡打粉，奶粉（如果有的话）混合均匀，中间打入鸡蛋
3. 将鸡蛋打散，打散的时候，鸡蛋会逐渐和面粉混合到一起，再中央形成一个较稀的面团
4. 将刚才的酵母粉水慢慢加入，用筷子搅拌直到形成一个不均匀的面团
5. 手上粘上一部分的油，揉面，揉到初步均匀即可。
   * 由于这个面特别的软，如果手上不抹油的话会很难揉。
   * 实在没法揉就等几分钟（醒面），水会自然在各部分间相互渗透，届时会好揉一些，不用和面团较劲。
6. 醒面10～20分钟。
7. 手上抹油继续揉面，直到把剩下的油都揉将进去。
8. 封闭发酵到2倍大，约要45分钟～1小时。

### 揉面备注

这个面团配方的粉水比大约在1.42～1.5，要知道，比较硬的面团（如面条面），粉水比约在2.17，比较软的面团（如制作包子馒头）在1.8左右，粉水比低于这个数字就较难直接揉了。如果没有厨师机，就遇到了不得不揉这么一团湿面的惨状。湿面团极其粘手，如果没有手粉或者油脂作为辅助，是没法揉的。

揉面的手法大致有两种，揉和揣，揉是用手指将面叠起来以后用手掌压下去，而揣是用五指将边缘的面叠起来或者捏到中间以后用拳头压下去。 揣面更加不容易吸油，借此机会可以给面整形但是对混合均匀无大益。而揉面相对更多的会将外层的面压裂而吸收更多的油，一般用在想要混合均匀的时候。

第一遍揉面的时候，面是非常不均匀的结构，一般先揣几遍，然后揉到均匀即可。 第二遍揉面的时候，由于会放很多的油，所以以揉为主，发现面团均匀并开始粘手的时候，就用揣整形准备发酵。

关于油脂，再开始揉面之前，可以在手上涂一些高熔点的动物性油脂（比如猪油（但是这样阿拉伯人就没法吃了））可以极大的减少满手面糊的情况。相应的，要在面团的用油中扣去这一部分的重量。

### 小面团制作

发酵完成以后，首先排气，将面团里的大气泡全部挤出去。 这些大气泡如果保留，烙饼时鼓起来就会特别薄，成为整个面饼的阿喀琉斯之踵，一旦破裂，蒸汽无法保留就再无鼓起来的可能。

将面团分割成70～80g左右的剂子，并且滚圆备用。

面团仍然是粘手的，但是组织既已稳定，这个时候用一些手粉就可以解决问题。

1. 这个面团放在冰箱里冷藏会更好
   * （未验证）冷藏以后的饼皮会比直接使用的饼皮更软
2. 这个面团（或者之后做成的饼皮）是可以冷冻的，可以成为特别方便的早餐。
   * 冷冻的面团解冻以后再擀一下

取出一个小面团，擀开，这时候会遇到阻力，因为面团自带较大的筋性，即，面团中的蛋白质（麸质）已经在水和盐的作用下结成网状结构，倾向于保持原有的状态，突如其来的外力并不能猝然改变其形态。

所以将面饼擀开一点后就静置数分，让它适应新的形状，形成新的结构，此时再擀便又能擀开些。

一般来说两遍就足矣，当一个小面团成为一个直径二十厘米左右的圆形小饼时，就可以开始烙饼或者烤制了。如果喜欢，制作面饼的时候可以在一面上撒上炒熟的芝麻，则成塔斯汀的汉堡胚。

## 烙制

只需要一个平底锅即可制作，首先预热平底锅，然后放入面饼，一面烙制到可以滑动的时候，立刻翻面，第二面也烙制到可以滑动时即再翻面。面饼应该微微起皱，甚至又多一点淡黄色的烙印，如果没有，说明温度不足，可再如此翻面两次直至两面封闭。 也不可焦黄，焦黄则说明火太大，两面都脆了时鼓不起来的。

若两面既已封闭，此时开大火，面饼则会自然鼓起。鼓起到一半的时候翻面，用铲子或者无情铁手将为鼓起来的部分贴住锅底，不消片刻就可以鼓的像个球一样。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-0b99c09567b7897f34dddf0a316e63a84dcce8a2%2F2023-03-04-17-13-05.png?alt=media)

## 烤制（未验证）

上下250度预热完成之后推入烤箱烤制即可，烤制这个饼的温度是高一点，时间短一点比较好，鼓起来即可出炉。

## 参考资料

* [塔斯汀慌了，中式汉堡做法原来这么简单！](https://www.bilibili.com/video/BV1Jx4y1c7sm/)
* [皮塔饼 Pita Bread 饼大如球 做法有趣 进来瞧瞧吧](https://youtu.be/_zVEKAtqfvk)
* [Pita](https://en.wikipedia.org/wiki/Pita)


# ボロネーゼ

これから、ボロネーゼの作り方をご紹介致します。

ソースの食材は：

* 玉葱（３００g）
* 人参（１００g）
* トマト（４００g）（缶詰の方が良いと思うけど、新鮮なトマトも良いと思う）
* 挽肉（牛、豚、牛豚合挽肉（あいびきにく））（３００gくらい）

香辛料は：大蒜数個、ローリエ、黒胡椒。

まず、大蒜を微塵切り（みじんきり）にします。玉葱、人参とを細かく切ります。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a89a619d2c9f70188d6498d14e85edd114eb2439%2F2020-08-16-21-58-08.png?alt=media)

そうしたら、微塵切ったの大蒜をバター（オリーブオイルもOK）と一緒に鍋に入れていきます。そうしたらコンロに火をつけて、弱い火で熱していきます。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6fa83a7c8c12611312e60fbe4b4b9d73a0a06630%2F2020-08-16-21-59-09.png?alt=media)

バターが茶色になる時（バターは焦げやすいです）、玉ねぎを入れていきます。玉ねぎをちょっと強い火で炒（いた）めて、途中で塩と黒胡椒を入れておきます。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-daffd024c1c4ac60a4419b4c2b58e576248b9fc9%2F2020-08-16-21-59-52.png?alt=media)

ちょっと焼き目をつけたまで炒めておきます。

後で、炒めた玉ねぎを取り出します。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-50bd2c2c5af1f9aa92daf57d4fd8a10aad790f74%2F2020-08-16-22-00-37.png?alt=media)

**鍋を洗わずに**油をつけて、肉をじっくり焼いていきます。肉もちょっと焦（こ）げた感じにしたら、も土鍋に移行する。

**鍋を洗わずに**ワインを入れます、鍋の茶色物質（焦げた玉ねぎと肉）をワインに取り込んで、後土鍋に入れます。

土鍋に細かく切った人参とトマト缶詰（私は[野菜ジュース](https://www.amazon.co.jp/gp/product/B000ZIMD2A/)買いすぎましたから、トマトジュースも入れますorz）入れます。

ローリエ二枚入れましたら、トマトを溶（と）かす感じまでよく混ぜていきます。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6f5d3068f2563741331bd4db6d8be042e6e15fb0%2F2020-08-16-22-01-11.png?alt=media)

ほんの少しだけ水を加えましたら、出来るだけ弱い火で一時間以上煮込（にこ）んで行きます（牛肉なら、三時間の方が良い）。途中二、三回混ぜておきます。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-58c0c740fcc17789b48cb3cd81c4c54b7aa4e592%2F2020-08-16-22-01-30.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c1c1a11dc9a0ae1f321bfb82dd86398f486c020a%2F2020-08-16-22-02-22.png?alt=media)

ソースの準備は以上です。

自分好きな麺（普通はパスタを使いますけど、わたしは中華麺が好き）を煮込んだら、皿に併せって、準備したソース一杯を付けて、粉チーズを振りかけます。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-6523f7892e5a5ba07d45cc920a15d71d9def8adf%2F2020-08-16-22-02-43.png?alt=media)

完成です。よく混ぜたら、食べて行こうと思います。


# 甜酒酿的制作

## 为什么要自己制作甜酒酿

其实就是馋虫作祟，为了吃一碗酒酿鸡蛋汤。

其实我本来是个（同样品质下）能买绝对不自己造的人，住在中国的时候从来都是去市场买，但是在日本实在是找不到靠谱的酒酿了。

首先考虑的是“甘酒”，以下是我尝试过的方案以及遇到的问题：

* 冷冻干燥的甘酒：风味严重不足，放到锅里一煮就化了，简直是闹着玩的
* 瓶装的甘酒：甜味特别重，甜到齁嗓子的那种。风味也还行（有点像小时候喝的速溶麦片），可惜和糯米制作的甜酒酿不一样。
  * 日本的甜酒根据厂商的不同大致有两种，一个是酒厂出品，一个是酱油味增厂出品的。酒厂的菌种偏向于产生糖（化酶），而酱油味增厂的菌种倾向于产生氨基酸。由于所用菌种不同，风味也会稍有差异，不知道是不是这个原因，没有做深究。
* 用糯米和干燥米麹自制甘酒：这下子有一点糯米的风味了，但是味道还是很奇怪，这是所有平替方案里面最好的了。只不过反正要发酵，所以干脆搞正版的比较好。
  * 和标准做法的高温“发酵”（其实应该只利用了已有的酶）不同，这里是低温发酵的，为了促进发酵，还放了很少一点面包酵母。

日式甜酒方案全部Pass后，我就乖乖地跑到中华物产店，买了台湾金兰的雪花酿，然而瓶装的常温酒酿就是和新作的酒酿有天壤之别，不太甜，反而有点酸，风味也很一般。

最后在经过漫长的研究（主要是研究酒税法，看看是不是合法）以后，决定自己制作。

## 制作步骤

酒酿的制作，一言以蔽之，就是用菌（产生的酶）去糖化经过了糊化的淀粉，以产生甜味和淡淡的酒味的过程。 所以所有的步骤，目的大致是：保证淀粉糊化的完全，保证菌种纯净、没有杂菌，以保证糖化、发酵过程的顺利。

### 浸泡糯米

糯米要加水浸泡到指甲一掐就断的地步，所以我一般浸泡一夜确保泡透。夏天的时候容易腐败，所以可以放到冰箱里浸泡。

### 蒸糯米

蒸锅上面放上笼屉布，将糯米铺平蒸熟。蒸熟的标准是糯米被蒸透，没有硬芯子。根据我的实验结果，要蒸30分钟左右，如果只蒸20分钟那就可能没有熟透。

蒸完以后建议闷上一段时间，因为蒸糯米的本意是要让淀粉糊化，长时间的加热有助于这一步骤。

关于能否用电饭锅煮饭，我也曾试过。电饭锅做饭的时候一定要少放水，然后减少煮饭时间（或者开“较硬”档），比起蒸出来的米饭，电饭锅的米饭拌曲的时候会更加困难一点，其余没有区别，甚至淀粉的糊化会更加完全。

### 拌曲入罐

拌曲之前要把米饭冷却到30～40摄氏度，否则酒曲会直接烫死。（顺便一提，日式甜酒的工艺温度居然在55度左右，应该是因为仅仅使用酶进行催化糖化而不是发酵）

我用的甜酒曲是Amazon买的，每斤米大概放2g酒曲即可。

拌曲的另一个关键点是要**用水**，这个在很多的教程里都没有明写。只有加水以后，才能把酒米拌到粒粒分明的状态。用凉开水即可，一斤的米大概需要用半斤水。加水和拌曲同时进行。

至于水的量其实没有什么定数，我的标准是拌到颗粒分明，酒曲也均匀了，但是又没有多余的水即可。一些地区（比如湖北或者四川）的醪糟工艺会在入罐后发酵前加入较多的水进行发酵，这也是可以的。至于水的选择，我觉得市贩的纯净水就很好，一时没有的话用凉白开也行。

拌曲完成以后就可以入罐发酵，记得压瓷实一些。大多数的酒酿都会在米中间掏一个窝子观察发酵出酒的情况，但如果你用的是透明容器的话可以不遵守这个传统（但是我仍然习惯这么做）。罐子在装米前要消毒，入罐以后要注意隔绝空气，这样才能保证发酵的质量。

### 发酵

发酵温度在30度，维持温度的最好方式是买个酸奶机（或者日本有甘酒机）。我也曾试过热水袋加棉衣的方式发酵，温度的控制很困难，温度高了会烫死酒曲，温度低了发不起来，温度忽高忽低有的时候会导致杂菌繁殖，进而导致酒米酸臭无法食用。

关于发酵的时间，一般来说：

* 24小时能感觉到发酵，能闻到毛霉菌的特殊气味（略微类似腐乳），出现酒液；
* 36小时左右的某个时间点会达到我认为满意的状态，这个时候米没有完全瘪下去，甜度也适宜，这个时候酒液会升高到接近顶部，大概是米饭高度的80%～90%；
* 36～72小时的时候，甜度会继续上升直到达到一个峰值开始下降，而在此过程中米饭的饱满度则不断下降。如果发酵之前你加的水比较多，你甚至可以看到酒米漂浮起来的情况。

发酵完成之后冷藏即可。

## 附录：在日本制作甜酒酿的法律问题

不幸的是，日本到今天仍然维持明治时期（对应中国清末）制定的陈旧且严格的酒税法，这就给我平添了很多确认步骤。

首先酒税法管的非常宽，除去工业医用的酒精以外，正常的食物里只要含有1%以上的酒精即认定为酒。早些年，莫说自己酿酒，就是买烈酒回来泡梅子酒都是违法的，直到2008年才改正了这个没人性的条款。

好在最后确认下来，结果如下：

* 如果你住在どぶろく特区（濁酒特区），只要不对最终产物进行沉淀过滤和蒸馏之类的操作，就可以放心的制作甜酒酿。
* 如果你不在这些特区居住，只要最终的产物里没酒精（少于1%）就是合法的，日本人也会自己在家制作甘酒。

所以，制作甜酒酿鸡蛋的时候要加水烹煮，利用这个过程让酒精挥发即可。

那么，烹煮能否彻底去除酒精呢？答案是否定的。根据[这份资料](https://nommagazine.com/%E7%83%B9%E9%A3%AA%E8%BF%B7%E6%80%9D%EF%BC%9A%E9%85%92%E7%B2%BE%E7%85%AE%E5%BE%97%E6%8E%89%E5%97%8E%EF%BC%9F/)([备份链接](https://archive.ph/U3KIt))显示：

> 這其中的差距是受烹飪步驟的影響：酒精是在一開始就和食物混合烹煮，還是在沸點到達後才加入。含有酒精的菜餚過了 15 分鐘之後可測得 40% 的酒精殘留，30 分鐘後剩 35%，而 2 小時又 30 分鐘過去，仍有 5%。

好在甜酒酿的酒精度一般只在3%以下，不要说烹煮，就是加水的过程都足以让酒精度降低到日本酒税法规定的1%以下。

参考以下资料：

* [酒税の世界　なぜ自分で酒を造ってはいけないのか？（後編）](https://kurihara-office.com/160126jikajyozo-2)
* [酒税の世界　なぜ自分で酒を造ってはいけないのか？（前編）](https://kurihara-office.com/160124jikajyouzou-1)
* [濁酒裁判](https://www.courts.go.jp/app/files/hanrei_jp/336/050336_hanrei.pdf)
* [酒税の収入は3.1％に落ちました](https://www.nta.go.jp/taxes/sake/shiori-gaikyo/shiori/2016/pdf/001.pdf)
* [自家醸造](https://www.nta.go.jp/taxes/sake/qa/06/33.htm)
* [どぶろく特区](https://ja.wikipedia.org/wiki/%E3%81%A9%E3%81%B6%E3%82%8D%E3%81%8F#%E3%81%A9%E3%81%B6%E3%82%8D%E3%81%8F%E7%89%B9%E5%8C%BA)


# 政治不正确

一些可能会喝茶的内容，谨慎观看。


# 90年代到21世纪初的商业环境

[为什么（感觉）现在的资本家越来越露骨，越来越不要脸？是否如此？](https://www.zhihu.com/question/444051237/answer/1734648868)

因为你没有见识过90年代到21世纪初的商业环境，和那个年代的老板相比，当代老板有一个算一个那都是大善人。

举几个例子

比如煤矿行业，那段时间的煤矿行业，基本上就是一个拿命换钱的行当，有没有命花你挣的钱完全取决于运气和矿井里的瓦斯浓度，渗水事故属于常态，以至于并不被视为事故，矿工常年累月在淹到脚脖子深的水里作业，采煤有指标，采不够煤别想出井。

矿井的支撑梁能省则省，并不存在什么承重力学和结构力学，地质结构更是想都不要想，一切以不会马上垮下来为标准，整个矿上文化水平最高的是老板，初中肄业，技术水平最高的设备是手电筒，井下瓦斯浓度以经验为准，经验来源是前几次爆炸事故，一座煤矿一年炸个两三次算是基本操作。

这算是有良心的，至少来这种地方刨煤，白面馒头和肥肉片子管够，工资不少结，死球了还给钱，有些绝症户就是冲着抚恤来的。

真正脏的还是那种奴工煤窑，坑蒙拐骗把智障拐来，拿大铁链子拴在井下，吃喝拉撒睡都在下面，干到死，完事了找个废井一埋拉倒，类似的还有黑砖窑，直接从附近学校绑学生用大铁链子拴着烧砖，为了防止逃跑，有挑脚筋的，有割舌头的，有把眼睛弄瞎的。

还有夫妻井，一家两口人在自家院子后面挖个大坑就开始刨煤，刨不动了就雇人来帮着刨，刨的深了又没啥文化，井架一崩所有人都埋在里面，亲戚来串门才发现人都没了。

再比如建材行业，建材行业是那个年代的世界上最接近武侠小说中“江湖”这个概念的领域，每个砂石厂都是一个门派，每个砖窑都是一个码头，每个卖涂料腻子粉的铺面都是一个堂口，每个租施工机械的都是一个舵把子，江湖关系盘根错节，各种恩怨情仇，你能想到的武侠小说桥段在这个养蛊大坑里都有。

渣土车，重载车，拉水泥的就跟镖局差不多，路上遇到车匪路霸了就得下来递烟说好话，塞钱的塞钱，磕头的磕头，到了工地了如果迟到了，工地老板说不要就不要，你的水泥就干在桶里吧，哦对了，那个年代监理很少，基本上都是跟包工头打交道，包工头基本上在工地上形成了一种基层治理模式，警察说话都不一定有用。

有些地方的建材行业和黑恶势力基本上可以划等号，业务发生纠纷或者堂口间互相冲突了，老板带着三两个骨干裹挟几百号人就能打到见血，扣人绑人，把竞争对手绑在电线杆子下面烤太阳，不给喝水，喊口渴了就倒半杯水，撒一大把盐，喝吧。

斗殴方式五花八门，砂石厂作业要用到炸药雷管，这就是一种路子，武侠小说里的江湖暗杀都是什么唐门暗器，喂以九幽五福，杀人于无影无形，这都是鬼扯蛋，真实江湖暗杀是趁你睡觉的时候半夜往你卧室里丢一捆雷管，让你热热闹闹的上路。

至于工地上组织什么钢管队，菜刀队，砂石厂组织什么护厂队，大车队，那都是基本操作，土枪砍刀那更是小儿科，上档次的械斗都是机械化的，你们见过挖掘机打架，铲车冲阵，压路机当攻城锤使的吗？还有把拖头前面焊上钢架直接拿去冲门的，哪次不见血？

你以为开公司的又干净？那些年，凡是做生意就没有不沾黑的，坑蒙诈骗，以次充好，官商勾结，假冒伪劣，侵吞国有资产，套取商业补贴，拖欠工资拖到有家公司承诺绝不拖欠工资能上感动中国，这些年整治老赖，各种手段一起上，甚至搞连坐，搞祸及子孙，有些人都能梗着脖子就是不还钱，你想想那些年没有这些手段的时候，老赖是怎么拖钱的，有多少企业是直接被老赖拖垮的？

还有拖工资，那些年拖欠工资属于常态，不仅拖欠工资，有些还倒找员工要钱，那些年商业环境不发达，岗位少，人多，工作不好找，好不容易有人找到了工作，进公司之后先让你交保证金，押金，这金那金交个大几千乃至上万，然后才让你进公司。

有良心的就当牲口使，然后三天两头按照各种自制规章扣工资，没良心的直接把手下员工的这金那金拿着就跑。在某些地方，这种开空壳公司，招人，收保证金，然后卷款逃跑的路子都形成产业链了，是什么地方就不点名了，免得又说我地域歧视。

还有就是侵吞国有资产，整个80.90.2000年初，国内一谈改革就约等于卖国企，啥都敢卖，啥都能卖，一切形势转改制，一切改制转卖国企，一切卖国企转低估低售，几百亿的资产几个亿就敢卖，卖完之后一顿折腾，先来一顿大下岗，再来一顿大抛售，然后表格一做，进大于出，一场国企改制扭亏为盈增进效益的先进事迹报告会就出来了。

官商勾结在那些年属于公开搞，属于先进经验，属于主动作为，属于被提倡被鼓励被支持的招商引资行为，哪儿像现在，去婚宴一起吃个饭随个份子还要打报告，那些年商业渠道不通畅，市场环境不完善，要想做成点事甚至必须官商勾结，什么倒批文，卖白条，转车皮那都属于基本操作，有些官商勾结的渠道说起来都匪夷所思，某领导认识了招待所的服务员，一来二去成了通奸关系，该服务员的丈夫得知此事，不怒反喜，极力给二人牵线搭桥，创造机会，甚至直接在家里开搞，过一段时间，该地有一项目，工程款几千万，该服务员的丈夫回老家找了搞承包的亲戚，让该服务员帮忙牵线搭桥向该领导讨要此项目，项目拿到之后，搞承包的资质不够，就去外地借壳，整个过程一律特事特办，有该领导罩着，一路都是绿灯，最后项目拿到手，该承包方也算是有良心，没有偷工减料，因为怕出事，反而加工加料，最后项目利润到手，分三份，领导一份，服务员家一份，承包方一份，该项目属于基础设施，因为加工加料，品质较为优质，群众满意，还成了典型，服务员又给领导拉皮条找了其它女人，自己从招待所辞职不干了，跟老公回家过日子，承包方靠着这个项目经验申请到了自己的资质，该领导性生活愉快，数钱数到手软，还有了政绩，违法乱纪官商勾结，但是最后所有人都获得了幸福，公私两全，你说这种事你上哪儿说理去？

这算是有良心的，没良心的呢？

那些年的偷工减料手段以现在来看简直不可思议，纸板水泥，细丝钢筋，地基桩数能直接给你减少一个数量级，那些年的工程质量，属于小项目小偷，大项目大偷，连重大基础设施都敢偷，98年大洪水，几个口子决堤的很大一部分原因就是豆腐渣工程，豆腐渣工程这个说法就是98年提出的，类似的还有现在已经逐渐淡出视野的黑心棉，地沟油，三聚氰胺，那都是90年代兴旺发达搞起来的东西，再说点小众的，用废旧皮革做的果冻，见过吗？用棕榈油和松柏油做的蜂蜜，见过吗？收死人的衣服拿来卖给活人穿，见过吗？铅做的螺丝，见过吗？甲醇假酒，见过吗？福尔马林泡的毛肚，见过吗？劣质漏液铅酸电池，见过吗？

当代中国社会上相当一部分人之所以对各种所谓的纯天然无添加和各种乱七八糟的进口货有一种宗教式迷信，很大一部分原因就是那些年充斥市场的假货留下的心理创伤。

还有走私，当年东南沿海走私之猖獗，基本上已经到了裂土封疆的水平，自古以来走私犯没有不穷凶极恶的，北美独立战争的由头之一，波士顿倒茶事件，就是一群走私犯因为英帝国调低了关税，导致他们的走私货卖不上价，血本无归，一气之下闯进合法商船，打砸破坏合法进口货物，把茶叶倒进水里，后来这些走私犯成了北美叛乱分子的骨干力量。

当年东南沿海缉私工作压力之大，不是现在能想象的，现在中国的海警实力强大，上万吨的海警船都有，可以在钓鱼岛和南海横冲直撞，当年中国海洋执法力量破碎而松散，实力弱小，规模不足，设备落后，管理稀碎，缺乏协调，号称九龙治海，走私犯的船舶先进又巨大，还高度武装，还经常混杂在渔民里，甚至于有些渔民本身就是半渔半走私，经常出现缉私力量开着小艇追击开大船的走私犯的情况，那些年的走私犯手中有大量武器，有些是早年建设民兵力量留存下来的，有些是从走私渠道流入，这导致他们的装备往往比缉私力量还要先进，缉私执法人员追不上，打不过，这些走私犯猖獗的跟海盗似的。

东南沿海地区的海岸地形地貌又复杂破碎，走私犯在当地偷摸建设私设码头和货运设施，甚至还有洞库形式和半水下式结构的，从外面看过去根本看不出来，在这些私设走私设施周边还有服务于走私人员的各种服务业，包括并不限于赌场餐馆窑子，还有包括赌场餐馆窑子一体化的一条龙服务，对了，一条龙这个词也是那时候发明出来的，甚至还有走私货物的交易所，俨然当代加勒比海盗，这些走私犯往往以宗亲乡谊和血缘关系为纽带，内部组织严密，杀人灭口毫不含糊，有卧底的执法人员暴露后直接被捆上水泥块沉海的，另外这些走私犯往往还利用其便利性，从事偷渡，贩毒，谍报等工作，被抓到了就武装拒捕，暴力抗法，等到积攒满了第一桶金之后就洗手上岸，用积累下来的资本从事其它合法行业。

有些走私案规模之大，金额之巨，涉案之广，从地方直达中央，号称不怕领导讲原则，就怕领导没爱好，当地官员上任，都要去走私巨擘家里做客拜码头，而且必须收礼，当地称之为喝茶水，不喝这杯茶他有办法让你在当地混不下去，喝了这杯茶你就收了他的礼，从此以后你就不干净了，跟他在一条船上了，上上下下都有他的人，你怎么和他斗？而且帮他办事，你有求必应，他就投桃报李，你家里老人死了他去帮你奔丧，哭的比你还像亲生的，财色烟酒茶，吃喝嫖赌抽，你要啥有啥，你子女是学渣他一顿操作能帮你送到重点学校去，海关港务地税工商交通一手抓，甚至于其它地方的人去当地做生意都得先去他家混个脸熟。

还在当地盖专用于接待的豪华场所，外表刷成全红，里面一条龙应有尽有，连接待女官员的王子都齐齐整整的。

所以每次看见有人喊加速我就想笑，这也配叫加速？你懂什么叫加速吗？你见过加速世界吗？和8090年代到21世纪初那种百鬼昼行的局面相比，眼下这些资本家最恶劣也不过偷偷摸摸钻法律空子搞搞集资，除此之外谁还不是个奉公守法的良好市民了？

现在走法律仲裁是真能讨到钱，放90年代，你敢去找包工头闹？腿都给你打断。


# 回乡杂记

## 回乡杂记

今天是2025年的最后一天，我在从河南到上海的火车上开始写回乡杂记。 我也不知道什么时候能够写完，反正旅途无聊，权当打发时间。

### 江苏

#### 便宜

回去补个衣服才10元，我补的是羽绒服，要划破后腰把袖子反着掏出来划开补好再缝起来，忙活好一阵子，10元。 剪发15元，很便宜（但是给我剪了一个锅盖头）。 但是如果我回去工作，我也很便宜。

#### 驾驶

开车在路上，每个人都要谋杀我。随意变道、骑跨车道开车。 我按照规矩在“停”的地方停止一下，后车的喇叭立刻就滴过来了。

#### 疫情的爪痕

疫情的痕迹消失不见了，没见到任何疫情时期的标语、核酸亭或者行程码之类的东西。

#### 手机卡

想去办新的手机卡，因为我现在的手机卡经常莫名其妙的就打不出去电话了（反诈）。 但是工作人员很诚实地告诉我8块的卡就是找封，所以中国移动就是借着反诈的名义惩罚低消费的用户来创收。

#### 我奶奶的手机

大约是2年前，大伯给我奶奶买了一个华为手机。自那以后，我那90岁高龄的奶奶就迷上了互联网（中国的互联网，并非真正的互联网）。

我觉得这也没啥，但是我发现我的奶奶对着一个视频在留言，留言的对象帐号是一个卖货帐号，大约是盗用了靳东的视频（也可能是AI？世界太乱），靳东在视频里温柔的说着：“我的好姐姐……” 我奶奶用手写输入法留言：“东弟，你不要这样执着，……” 给我劝下来了。

我的奶奶活了九十年，面对互联网的时候单纯的像个3岁的孩子。互联网上说什么，她就信什么。别人发：“我的大网红，我知道你很忙，但是请你抽出一点时间……”，她就真的相信对方叫自己大网红。而这甚至都不是私信，只是别人发的视频。对面盗用刘亦菲的图片，说自己是：“对联刘亦菲”，她照单全收。

于是我以互联网老法师的状态给她开展了45分钟的网络常见套路入门课。

***

我也想过，要是有一个铁拳机构整治一下这种针对老年人的诈骗，那该多美。但是连叫人一声“大网红”或者盗用靳东的视频都要吃铁拳，那我觉得言论之自由也岌岌可危。

根本的方案还是对老人的关怀——告诉他们什么是真的什么是假的，这个任务应该让子女完成，也可以让社区（政府）负责一部分。

#### 关于奶奶手机的浏览器

这个抽象的手机值得说一下，我自从被魅族系统里无处不在的广告恶心到（我知道魅族其实不是最差的！）以后，我就再也没有用过国产手机。 几年不见，国产手机的发展已经让我咋舌了： 我打开系统自带的浏览器，上面是百度的搜索框，下面是一水儿的新闻视频等等的推送。 重点来了——没有地方输入网址！一个浏览器！！不能输入网址！！！

我大抵是无法再用国产手机了。

### 厕所

中国没有水洗马桶很不爽，对我的痔疮不是很友好。好在上次我妈来日本以后觉得此物实在是好，家里也装了，让我能够舒适一些。

### 河南

* 我的岳父岳母待我确实如自己多了个儿子一般。我的岳母总怕我饿着。
  * 如果我把桌上的东西吃完了，就说明不够，一定要吃到剩下来为止。
  * 这样的热情实在是容易叫人撑坏了，所以就连岳父大人都表示这不对。
* 河南烩面汤鲜、面劲道，基本是我理想中的面了。苏面汤头美味但是碱水面不劲道，西北面劲道浇头霸道够味，但是缺了高汤的细腻清鲜。只有河南的羊肉烩面把两者结合的很好。
* 疫情的痕迹消失不见了，和江苏一样。
* 看到了天鹅，大家都在拿面豆（炒琪，一种用干干的细细的泥土炒制的面食）投喂天鹅，许多天鹅疑似已经吃撑了（和我一样）。
* 看到了骆驼队，骆驼队号称他们要重走丝绸之路。事实上也确实从新疆走到了河南了。西安和洛阳为了谁才是丝绸之路的起点吵起来了，西安人似乎是有点凶，最后骆驼队没有过西安城区，最终目的地也设置在了洛阳。
* 骆驼队放着红歌前行，其实是为了直播能够存在，也便于和各地文旅局等等合作搞政治正确护身符，宛如文革。即使如此，也不能保证。
* 有的主播说着说着直播就被封禁了，而我们完全看不出违规在哪里。怪不得抖音上大家都不会好好说话了，大家不会好好说话是因为不能好好说话。

## 回日本的旅途

去上海坐的是绿皮车（K）。很多年没有坐绿皮车了，由于中日关系航班减少的原因，我没有定到晚上的票，今年便又坐了卧铺。 我这次坐车仿佛乡下人初进城，不知道现在已经完全不需要取票了。由于之前高铁网购麦当劳的体验很差，选择少，而且非常贵，所以我还是带了泡面和火腿肠，还有岳母给我准备的卤牛肉鸡蛋等等，就如同我过去那么多年坐绿皮车一样。

在雪中等了一会车，我看到绿皮车的座票上少有女生，甚至年轻人也很少，以四五十岁的中年男性为主。

火车上和大家聊天，对房价和就业形势大家有所共识。一个词形容现状就是萧条。中国实质上是发生了通缩的，只是政府不承认，创造了一个新词（忘了，好像是物价低增速时代？）来代替通缩。

好不容易买了软卧并且还是下铺，但是没有睡好。列车的震动很大，侧卧的时候床在扇我的脸，扇了一晚上。暖气也热的我浑身是汗。好不容易熬到了5点半到站，我去肯德基坐了半个上午，就启程去日本了。


# 关于中国最近一些强奸案的感想

## 争议不断的“典型案件”

近几年，中国的强奸诬告冤案（至少称之为争议案件）高发，在这些争议案件中，司法的严谨性似乎正让位于某种“惯性”或“大局”。

### 山东房洪彪案

2009年，山东沂源县男子房洪彪被前妻举报，称其多次奸淫当时未满14周岁的亲生女儿。2010年，一审法院判处其有期徒刑6年。检察院认为量刑过轻提起抗诉，二审改判为11年。房洪彪在狱中始终不认罪，其女儿成年后心怀愧疚，坦白当年是在母亲的殴打和威逼下做了假证。2019年，法院再审认定原判事实不清、证据不足，改判房洪彪无罪。房洪彪此前已服刑超过8年（3104天），最终获得了约132万元的国家赔偿。

### 南昌锜振东案

2022年，南昌男子锜振东与一名女性发生性关系。事后女方报警称被强奸。男方提供了录音和证据，证明性行为过程中女方处于“上位”，且表现主动，认为这证明了女方是自愿的。法院审理认为，女方同意发生性关系的前提是认为两人会 **“确定恋爱关系”**。但男方在事后拒绝确认关系，法院判定这属于女方产生了“动机错误”，导致其先前的同意无效，进而认定男方行为违背妇女意志。但是这就引出了一个很可笑的问题：锜振东和这名女子谈恋爱的话就不构成强奸，那么谈多久的恋爱呢？3个月？还是3小时？

### 大同订婚处女强奸案

这是最近讨论的最多的一桩案件了，我觉得有必要把这个案件完整的记录在这里。原因很简单：我看过的帖子、证据都因为某种神秘力量逐渐在互联网上消失，取而代之的越来越多的洗地文章。 好在有人不停的接力，我现在还能看到许多证据和质疑，但是如果我不做好备份，那么这些内容也会从互联网上消失。 无论如何我不希望我丢掉这些内容，我希望他们能在我这里得到一份备份。

简单来说，2023年，山西大同一对青年男女在举行完订婚仪式并支付部分彩礼后，在婚房内发生性关系。事后，女方要求男方在房本上加名并支付剩余彩礼，未果后报警控告男方强奸。男方拒不认罪，但是检察院按照强奸罪的判刑下限判了3年，2026年5月4日，当事人（男方）出狱，去高院上诉。

这个案件立刻引起了轩然大波，其实强奸案的诬告多了去了，但是这个案件广泛讨论是因为：“订婚强奸”，和事后检测女子“处女膜完好”。

先说第一点，一般来说，关系的亲密程度大约是：认识->朋友->恋爱->订婚（有的时候跳过这一步）->结婚。订婚是仅次于结婚的亲密关系，要是比这个还要再震撼一点，那只能是洞房之夜丈夫强奸妻子了。当然，按照法律的观点来说，只有领证和没有领证的区别，所以为本案的糊涂判决洗地的文章大多集中在宣传“订婚也不能代表性同意”。且不论“订婚也不能代表性同意”这种事情本来就和存在某个连续函数（比如Weierstrass函数）处处不可导一样，特例里面的特例。本案的焦点根本不是性同意，而是证据不足。

第二点经常被洗地的是针对本案没有任何确凿的物证的洗地，本案太多的证据都证明没有发生过性行为，而且这是一起仙人跳。

* 事后检测女子“处女膜完好”
* 女子内衣、体内均**没有**检测到男方的DNA
* 仅仅在床上检测到混合DNA，且不说没有公布具体的混合类型，退一万步讲，就算是两人的，顶多也就证明女方在床上裸露过。

许多的洗地都围绕：“检测技术不是100%准确，没检出不代表没有”，但是没检出也不代表有啊。这个世界上体内和内衣上没有我DNA的人有很多，总不能说我强奸了他们吧。

而且本案还有诸多疑点：

* 当被告律师要求检方确认女子放火时使用打火机的来源，检方回避了。如果打火机是女方带来的，一个不抽烟的良家妇女，随身携带打火机干什么？难道没有事前谋划吗？
* 事后官媒公布电梯间内两人拉扯的视频进行洗地，证明存在暴力胁迫行为。但是所谓的拉扯发生在事后，恰恰是这个视频证明了女方身上的外伤来源于事后争执拉扯。但是官媒遣词造句及其用心，称其为“案发当时的视频”。
* 事前两人在电梯内的拥抱亲热行为被无视了
* 事后围绕彩礼和房子加名这种明显是仙人跳的行为也被无视了
* 房间在14楼，男人下楼回来需要5分钟左右，她为啥不跑，只要走步梯，男人回到房间发现她不在，想找就会非常困难，而是男人回来才跑引发拉扯？如果说被反锁，但是现代商品房防盗门按照消防标准是不能“室外锁住，室内无法打开”的。

这个案件往小了说，只是无数的冤案中的一个小案件，但是因为“订婚”，“事后还是处女”，这个糊涂案件吸引了无数的流量，而人民群众继彭宇案之后，又一次亲眼见证了司法机关是如何“疑罪从有”的。见证了公诉案件无论多荒谬翻盘都非常困难的。见证了女方如何只要靠一张嘴就能将男性送进监狱。

相关知乎回答，新闻及其备份链接：

* [山西大同订婚强奸案到底是不是冤案? - 将离的回答 - 知乎](https://www.zhihu.com/question/1903734910818165193/answer/2030263156694627179)
  * [备份链接：山西大同订婚强奸案到底是不是冤案? - 将离的回答 - 知乎](https://archive.md/4AkUP)
* [山西大同订婚强奸案男主出狱了，这个案子怎么看？ - 吃瓜群众杠精的回答 - 知乎](https://www.zhihu.com/question/2032851805076452779/answer/2033399672774111370)
  * [备份链接：山西大同订婚强奸案男主出狱了，这个案子怎么看？ - 吃瓜群众杠精的回答 - 知乎](https://archive.vn/03ANR)
* [订婚强奸案维持原判：男方拒不认罪，其母亲因公布女方隐私遭训诫](https://m.bjnews.com.cn/detail/1744784672168578.html)
  * [备份链接：订婚强奸案维持原判：男方拒不认罪，其母亲因公布女方隐私遭训诫](https://archive.vn/AXSbQ)
* [订婚等于性同意？“捞女恐慌”正蔓延？中国山西订婚强奸案掀争议](https://theinitium.com/20250422-whatsnew-mainland-shanxi-raping-fiancee-case-zh-hans/)
  * [备份链接：订婚等于性同意？“捞女恐慌”正蔓延？中国山西订婚强奸案掀争议](https://github.com/TsingJyujing/blogs/blob/master/z-spam/archive.vn/tmv2D/README.md)
* [你认真看了“大同订婚强奸案”的细节吗？](https://finance.sina.com.cn/cj/2025-04-18/doc-inetquyt2341780.shtml?froms=ggmp)
  * [备份链接：你认真看了“大同订婚强奸案”的细节吗？](https://archive.vn/cFVRx)

## 被诬陷了应该怎么做？

这里我参考了知乎上许多网友的建议和经验，进行了一个总结。如果你是被诬陷的，那么解决方案只有一个——**跑**。

先跑出省、再跑出国，免签国家玩起来！

第一，千万不要去当地派出所。

去了派出所，人往检方一送，到时候别说你没强奸，就算女的还是处女，或者有你们当时恩爱的录像都没有用。哪怕你有不在场证明也是开车百里远程作案。

这里有几个点需要细说的，第一：警察不送检不就行了？警察有权利不报捕和移诉，但他不这么做。不管有理没理都移送检察官。为什么要移送检察官呢？只是因为怕信访投诉。

送过去以后，检察官说不捕才行。可到了检察官手里，你的案子要排队，前面的还没办完。挨到你的案子时，已经关了半年\~1年了。如果 做出不起诉决定 无罪释放，那得国家赔偿给你，算检察官和派出所失职，国家赔偿也是地方政府的损失。所以你做个认罪认罚，实报实销，关了X年就判X年，你马上能走。不愿意的话，他们也能做到弱证据判实刑。

第二，为什么没有证据也可以判刑，什么这么离谱？

原因很简单：因为在判决有罪之前你已经坐牢了，如果你没有罪，难不成是公检法弄错了？还得国家赔偿？想什么呢？必须不能够啊！所以错的一定是你，到时候就是实报实销，判X缓X，至于和解的赔偿和案底只能委屈一下你了。如果不认罪那就不缓刑，关到嘴软为止。

那么为什么枉法也要保证它们的“正确率”呢？这是一个深层次的问题，追问的是你的合法性是来自于民选还是来自于正确。

我掰开来细说说。东大找不到例子我们找美国的例子凑合一下。特朗普昏聩胡搞大家都知道，但是特朗普为什么那么迷之自信？（只因）为他确确实实是选上来的。有这个合法性在手里，他可以不用顾忌正确与否，他需要做的只是以某种方式践行选举时候的承诺就可以了。当大家质疑特朗普的时候，特朗普和他的支持者可以说：“你挑的嘛！偶像！要愿赌服输啊！”

但是另外一些人，他们的合法性只能来自于他们的“永远正确”，我们换一个说法就是“英明”。可我们知道，人也好组织也好，都不可避免的会犯错。那么为了他们的“永远正确”，只能牺牲一下当事人了，骂名也当事人来担。

按照道理讲，官媒和司法是两套系统，官媒没必要为司法系统站台，但是如果换一个角度想，他们的存在都依托于“永远正确”，这种行为也就不奇怪了。

那为什么跑就能跑得掉呢？出省不还在中国吗？出国也还得回来啊？

是因为没有扎实的证据，警察很难跨省抓你。跨省抓人分合规和违规两种，前者需要确实有犯罪证据，走正规流程。你和本地警察不是平等的，但是本地警察和外地警察是平等的，你就可以借助外地警察帮你挡住这一波。

至于后者，一半针对企业家或者作本地企业的保护伞，一般用不到普通人的头上。

那么跑出省就可以了，为什么我还是建议你出国呢？这是一种更强硬的宣誓：“你们尽管接这个案子吧！但是考虑好，这个案子可能永远结不了，不仅难以给你们升官，还会影响结案率。”

这个时候当地警察反而就会站到你的一边，“劝”女方放弃立案。其实他们从没有站到女方或者男方一边，他们从来都站在KPI的一边——工作过的人肯定知道我在说什么。

## 感受

最容易想到的是男女对立，但是男女对立已非一日两日。

这些最令我不寒而栗的，并非某个具体判决的谬误，而是这种明显背离证据逻辑的认知正被肉食者当成喝水吃饭一样寻常。 法律的“正义”不再依赖可验证的客观事实，而是取决于KPI和社会影响，法治就失去了它的尊严。在我国各种刑事案件大幅下降，经济案件上升的时候只有强奸罪一枝独秀，比许多人口中的强奸大国印度还要高的时候，一定哪里出了什么问题。

对于普通人而言，当法律不再是保护神，而成了不可预测的风险来源，为了这种风险，社会要损失效率。

现代社会的效率建立在法律法规带来的可预测性上。举个例子，90年代车匪路霸猖獗，物流成本也是居高不下的。警察用心办案，抢劫大多会被抓+被判刑，所以大货车司机才可以不用带刀或者雇保镖，物流成本就能下来。

其次，上文提到，如果被诬告，唯一的办法是跑。我们不谈带来的经济损失，要知道被诬告的人要跑，真的强奸案犯也可以跑。这种无奈的知识如果得到普及，那么对真正被害的女性带来的只有麻烦。

可是诬告他人的女性并不会考虑到她们的女性同胞的处境，她们只考虑自己的利益。而大多数的女性却还要站在她们一边，为她们站台撑腰，因为那也是女性同胞。

我作为一个旁观者，只能看着这一切演进，无能为力。


# AI所感

今天是2026年的4月2日，凌晨，我开始写这篇文章。这篇文章涉及到的所有技术，也仅仅局限于今天的AI发展水平，未来或许会有变动。

## 为什么写本文

最近（生成式，下略）AI已经不能称之为大热了，可以说是红的发紫，紫的发黑，黝黑发亮。AI也是我近几年见过的最具有生产性，最激动人心的技术之一了。自从ChatGPT 3.5取得成功以来，资本的关注迅速提升。企业用AI为借口融资，也以AI为借口裁员。 他们用从银行借来的钱，购买了尚未被生产出来的显卡与内存，来满足不知道用户在哪里的未来算力需求。 资本从来都不想被时代落下，上一次卷积神经网络的AI爆发如此，虚拟货币，NFT，元宇宙也是如此。

更加重要的是，AI确乎展现出了它的神迹，它已经通过了图灵测试。如同珍妮纺纱机一般大幅度提高生产效率，淘汰落后产能已经就在眼前了。

我作为一个码农，平时也大量使用AI写代码和文档，Claude和Codex我都使用。我也从中感受到了莫大的便利。 但是，面对最近的AI热，我总想说一些使用体验——虽然有些可能近乎泼凉水。

## AI有什么缺陷

为什么开篇要说缺陷？有两个原因：

第一、是我们已经足够熟悉AI带来的各种神迹了，无须赘述。 第二、是我们需要清晰认识到AI的边界，才能更好的利用AI。

### 幻觉

首先，AI幻觉的问题从来没有被真正的解决。AI的幻觉问题在最新的大模型中被抑制了，但是仍然如同斩不断的乱草一般。

相信大家一定看过那张梗图：“我家距离洗车店50米，我应该开车去还是走路去？” 好多的AI（包括我亲测了Gemini）都推荐走路去。 为什么？因为简单来说就是P(走路|50米)>P(开车|50米)，当然不是很严谨，大家看个意思好了。50米这种短途举例在开车中不常见，而问题本身更是可以上弱智吧的程度，导致AI从来没见过。 AI仍然是基于统计的方式进行训练，那么就不可避免的有统计的问题。AI从来没有真正拥有过逻辑思维。

如果是一个人，应该怎么回答？

* “不开车去你洗你自己吗？”
* “你是要去洗车店上班吗？”（老白：对啊）

这是训练模型的时候的胎里毛病，不是增加数据量或者增大模型就能解决的。

除此之外，AI没有长期记忆——这一点靠MCP工具以及上下文压缩等等外挂技术已经有所改善了。

至于我工作中，遇到的缺陷主要还是来自于幻觉，AI会幻想不存在的API接口，会尝试把问题糊弄过去，头疼医头脚疼医脚。 在设计程序的时候，AI并不是自顶向下的设计，导致AI写出来的代码冗长繁琐，过于拘泥于很小的细节。

### 需求

与其说是AI的缺陷，不如说是软件工程本身的问题。这里面涉及到需求的暧昧性和测试覆盖的范围。

首先说说需求的问题。

比如说，我让AI写一个购物网站，我给AI的预期是：当用户跳转到购物车页面的时候，默认勾选所有活跃的项目，所谓活跃项目，就是用户最近30天内添加或者修改的项目。 这个时候AI吭呲吭呲的写好了，测试的时候发现，写需求的时候忘记写了，要先检验一下购物车里的商品是不是还都有效，否则一下单，后端API出错了。 好不容易加上，结果又有问题，还得检测一下商家的库存够不够，否则一下单，商家库存变负数了。 这些还是明显会想到的、会导致错误的部分。其实还有一些不会出错的小设计问题，是需要人类反馈问题以后修改设计文档的，那就更多了。

这样的问题人类开发者也有，我们即使假设你有了完美的AI，需求的确定本身不是毫无成本的。

其次是，AI自己写单元测试，通过了以后就真的没问题了吗？

举个例子，我让AI写一个素数判断函数，AI写出来以后测试了 2,3,4,5,6,7,8 都对。但是9可能就出错。 当然这个函数同样存在输入1的时候要不要返回结果，输入0和负数怎么办等等需求问题。

### 验证

软件写好之后，验证这个软件是否按照预期工作的成本——也就是测试成本，被严重的低估了。

我经常看见的一个论调是：以前写汇编，后来写C，用编译器生成汇编，再后来写Java，内存也自动管理，至于Python就更像自然语言了。 现在没人写汇编写机器码了也没有任何问题，所以AI革命以后自然语言会成为新的编程语言。我一直以为这样的论调Too young too simple, sometimes naive!

我们必须关注一个程序写好之后到底是不是按照你的预想在工作，比如用Go，Java或者Python这样的高级语言，我们确实不用关心底层的机器码（或者字节码之类）了，但是无论哪一种语言，其行为总是确定的，哪怕是个BUG，也是可复现的。像C++中的未定义的行为或者单片机跑飞了，都是少数情况，而且大多源于访问了不该访问的内存。使用高级语言之后这种事情是很少的。

至少我可以保证我创建了一个数组之后这就是个数组，不是HashMap或者HashSet。

但是AI做不到这一点，现在主流的生成式模型的输出是在概率分布上采样生成的，采样这个行为本身具有不确定性。即使我们固定了Hash，你也只是保证针对这样的输入能得到固定的输出，你甚至无法保证对于类似的问题（比如改一下语序）就能得到一样的回答。

这种情况下AI的生成必须被逐字逐句的校验，被人类理解。

然而，这是一个痛苦的过程，我经常是让AI写300行代码，理解，然后指出问题，然后AI删了100行，新增150行，我继续阅读修改的部分。 最后，AI完成了一个500行的程序，但是我Review了2000行的代码。我经常痛苦的想，早知道如此不如我自己手写来得快。

## AI能做到什么，我们应该怎么使用AI

其实在我看来，AI最擅长的还是飙垃圾话、小片儿汤话。 比如说写公文（类似于“我们需要拿出新举措，聚焦新态势，切实把握群众所需所想”这种鬼话），翻译文件，或者技术文档里面要向读者阐释基本的东西但是懒得自己写的时候。

如果是进行软件的开发，一定要：

1. 在沙盒里面运行或者严格限制权限，毕竟你也不想被AI `rm -rf /` 对不对？
2. 要能够切实检验AI的产出，而且是效率越高越好。比如回归测试，比如直接打开AI写的前端页面试看看。如果能让AI自己执行那就更好了。
3. 自己一定要有清晰的设计思路，如果你糊弄AI，那么AI也会糊弄你。我曾经尝试用AI在我不懂的领域写代码，那是一个在仿真器里面添加新算法并且测试看看性能如何的工作。结果就是，AI写的代码到处都是漏洞，但是在我自己搞懂项目结构和每个变量的概念之前，我也不知道AI到底犯了什么错误，我想AI自己也不知道。
4. 一定要理解AI干了什么，一定要逐句Review（审阅）AI生成的代码，不能因为代码是AI写的就偷懒不去弄清楚。那样的话即使通过定义测试、回归测试等等方法确认了AI的产出，但是代价是整个项目熵增了，以后的维护会越来越艰难。

## AI对我们的未来有什么影响

上面是一些技术相关的碎碎念，那么下面就更多的是政治了。

最近甲骨文也好，Amazon也罢，都在以发展AI为借口裁员。其实在大企业工作过的我们知道企业内部的低效与抽象根本就不是AI能治得好的。企业的问题在于长远目标的设立，通过这个目标获取未来的一席之地；而企业运营中实际关注的却是短期的盈利。不然怎么可能就这么巧，大家突然就都觉得AI是自己的长远目标了呢？

比如甲骨文，我理解的甲骨文的Oracle数据库虽然也是屎山源代码（听说），但性能就是很强，变态的那种强。AI可以用来重构，可以用来整理源代码和测试用例，甚至可以基于过去的经验开发新的数据库，但是唯独不是自己去搓AI。当然，我只是举个例子，甲骨文也是大企业，没准有自己的想法和其他的业务。

根本的原因还是在于资方也好，公司的高管也罢，都愿意追新潮，走捷径。有的是脑子只能理解新潮技术的表层，有的则是懒得说服前者去沉下心来经营更难的，更大的计划。

我相信不是大公司的领导不懂技术，而是这些技术大牛需要给手里握着钞票的，脑满肠肥的老登解释自己怎么样才能保证盈利。而老登的见识则不足以让他们理解新潮技术以外的更加深刻的东西，于是你就看到一众企业像是被饵料吸引的鱼群，时而冲过去撕咬虚拟货币，NFT，时而冲向元宇宙，最近又冲向了AI。

AI热也好，AI裁员也罢，根本的原因不在于AI，而在于急功近利的资本，在于严重不对称的劳资关系和巨大的贫富差距。

### 一个预测

最后还是说一下我的预测好了：AI将会大量取代低阶的岗位需求。AI相当于给每个程序员配了一个脑子不太好但是干活贼快的免费学徒，企业暂时失去了招新培养的动力，而且考虑到要买的显卡，现金流也不是那么充足。这样一来，市面上受过传统训练的工程师数量会逐渐减少，随后招聘成本上升，企业逐渐意识到培养新人更加便宜的时候会重启招新。但是考虑到AI的影响，市场仍然会相对萎缩一些。

考虑到现在市面上工程师过剩的局面，这个过程可能要持续几年。也就是说，这几年的计算机的毕业生会更加难以找工作了。而且更悲催的是，即使等到市场回暖的时候，企业也一定更倾向于招聘应届的，身体皮实的年轻人，这几年的计算机学生可能会成为“冰河时代”。考虑到这一点，及时转变方向或者读书是比较好的解决方案。

市场方面，定制化开发的成本将会下降，可能会卷死一批外包企业——毕竟他们糊出来的代码和AI难分伯仲。但是整体系统——特别是长期使用的大型系统的设计，以及靠近底层的部分，仍然需要人力进行大量的设计工作。

我相信肯定有智障企业觉得啥都可以用AI代替然后把程序员开得七七八八然后靠PM全面拥抱AI的。我100%相信这些企业会在未来为它们的愚蠢付出相应的代价。

愿我们迎来的是一个美好的未来。


# 让我们试着发明一个传统医学

## 为什么我要反对传统医学

首先我要说明，我讨厌现代仍然存在的大多数的传统医学，我认为它们不该作为医学体系存在。

我之所以说大多数，是因为我不确定是不是有某些地方传统医学有和现代医学一样的双盲实验之类的体系。 之所以只讨厌“现在仍然存在的”传统医学，是因为我们不能用今天的标准去评价古人。更不能否认古人在医学上的思考和努力——即使在今天看起来过于幼稚。

## 为什么我们要试着发明一个传统医学？

本质上是解构传统医学，传统医学已经和文化绑定的太深了，以至于大家很容易认为它就是对的。以我比较了解的中医为例，其体系下有阴阳五行之类的别的宗教也在用的玄学概念。 幸或不幸的是，我们从小看过很多武侠剧或者僵尸片，在那个奇幻的世界里阴阳五行确乎是有用的。加上民间文化里面也多有这种成体系的故事，导致传统医学更容易被我们接受。

所以我们更容易接受朱砂、夜明砂这些”药物“，也有地方还有童子尿煮鸡蛋，但是让我们去饮牛尿，就会有些抗拒。事实上，牛尿的危害再严重，也比朱砂这样的重金属好些。

我们不妨用最原始的方式去重新发明一个传统医学，让我们审视传统医学的诞生过程。这样也可以对别的传统医学去魅。

## 契机

我们今天都知道，纤维素虽然不能被消化，但是对人类还是听重要的。纤维素（比如蔬菜）吃的少了，荤菜吃的多了，容易拉不出屎。

我决定将这些发现纳入一套框架，重新发明一套传统医学体系。 这套医学体系里面有三个属性：分别是剪刀，石头，布。因为是三种元素相生相克，所以我称之为三行医学。

举个例子便秘我们认为这个病症就是属石头的，这个时候如果吃属剪刀的螃蟹容易加重病情，这个时候我们要吃一些宽大的叶子的蔬菜，他们是属布的，布克石头，所以可以解决便秘。

（以下基本是AI生成的，因为编写这些东西还是让我感觉和吃屎差不多，实在是绷不住）

（我再给某些大聪明多说一句，这些都是扯淡，你要是信了那是你自己的责任）

## 剪石布三行医学

这套体系可以叫“剪石布三行医学”，核心口诀就一句：

> 石克剪，剪克布，布克石。

治疗总则是：病属哪一行，就用克它的那一行去治；误用被它克的那一行，容易加重。

便秘属石头，螃蟹属剪刀，石头克剪刀，所以吃螃蟹会“以敌之敌为友”，被石头病邪反克，越吃越堵。 大叶蔬菜属布，布克石头，所以能通便。

### 三行总纲

#### 石头行

性质：坚、硬、沉、涩、凝、寒。 主：堵塞、硬结、骨、牙齿、大便、结石、关节僵硬、怕冷。 代表食物：核桃、栗子、山药、糯米、芋头、蛋黄、奶酪、花生、莲子、芡实。 常见病：便秘、结石、骨刺、鼻塞、寒性关节痛、闭经、囊肿。

#### 剪刀行

性质：锐、散、动、辛、尖、风、火。 主：刺痛、痉挛、神经、筋、指甲、咽喉、炎症急性期。 代表食物：螃蟹、虾、辣椒、姜、葱、蒜、韭菜、醋、柠檬、山楂、咖啡、酒、薄荷。 常见病：偏头痛、咽喉刀割痛、肌肉抽搐、风疹、经前刺痛、焦虑失眠。

#### 布行

性质：柔、润、宽、缓、湿、黏。 主：皮肤、肌肉、黏膜、津液、水肿、痰湿、松弛。 代表食物：大白菜、菠菜、生菜、油麦菜、海带、紫菜、冬瓜、西瓜、梨、蜂蜜、燕麦、木耳、银耳、豆腐。 常见病：腹泻、水肿、肥胖、痰多、皮肤松弛、乏力、白带多。

### 三行诊断

#### 看脸

* 石头脸：面色偏暗沉，下颌方，表情少，像一块石头。
* 剪刀脸：脸瘦，眉紧，颧红，眼神锋利，像随时要剪人。
* 布脸：面浮，眼泡肿，皮肤松，像湿布盖在脸上。

#### 看舌

* 石头舌：舌质暗，苔厚如石板。
* 剪刀舌：舌边尖红，有芒刺，像剪刀尖。
* 布舌：舌体胖大，苔滑，像湿布。

#### 号脉

* 石脉：沉硬，按下去像按鹅卵石。
* 剪脉：弦紧，像剪刀一开一合。
* 布脉：濡软，像按在一块湿布上。

#### 常见病举例

**1. 便秘**

属石头。 宜：大白菜、菠菜、燕麦、海带、蜂蜜。 忌：螃蟹、虾、辣椒、酒。 方子可以叫：布叶承气汤——大白菜叶、菠菜、燕麦煮汤。

**2. 腹泻**

属剪刀。 宜：核桃、栗子、山药 忌：西瓜、梨、蜂蜜 方子：蒙脱石磨成粉末，用水送服。

**3. 风寒感冒**

常见是外石内剪： 外面怕冷、无汗、身痛，是石头闭表；里面咽痒、喷嚏，是剪刀动风。 治疗先布克石：热粥、泡脚、盖被发汗。 再石克剪：用莲子、山药收内风。 如果一上来就吃大寒大布之品，可能外石虽解，内剪更乱。

**4. 水肿、肥胖**

属布。 湿气像布一样裹住身体。 宜：冬瓜皮、芹菜、豆芽、山楂、陈皮。 忌：糯米、肥肉、甜食。 方子：剪芽利湿汤——豆芽、芹菜、冬瓜皮煮汤。

### 三行传变

疾病会传变：

* 石极生剪：堵久了生热动风。比如长期便秘，最后痔疮出血。
* 剪极生布：乱动久了耗气生湿。比如长期焦虑，最后暴食发胖。
* 布极生石：湿痰久了凝聚成块。比如痰湿体质久了长结节、结石。

（不行，我用AI也绷不住了，到此为止）


# 日本的“儿童模特”生意

每次去日本的平价家庭餐厅Gusto，都能在放纸巾的盒子上看到儿童模特试镜的商业广告。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2a36e68fd27ecf02746ffcf678873d8e473b3059%2F2026-05-05-01-14-38.png?alt=media)

我的第一个直觉是这肯定是一门大生意，但是这个生意到底是怎么赚钱的，我暂且蒙在鼓里。

于是简单调查了一下，希望抱有类似想法的家长能够谨慎行事，珍惜自己和孩子的生活费。

## 儿童试镜的千层套路

首先我调查了一下其背后的公司“株式会社アドニティカ (Adnitica) ”，由于没有上市我也看不到财务相关的信息，可以查到的是大约在2013年这家公司就开始活动了。（确实我很早以前就在日本看到这个广告了，只是之前一直忽略了）

简单来说，就是以“免费试镜”为入口，通过后续一系列所谓的“收费服务”来赚钱。完全是利用家长对孩子成名的期待来层层筛选付费用户。这一点不难想到，但是这个局设置之精妙，值得分析。

### 广告投放

首先是投放的地点，Gusto的定位是一家廉价家庭餐馆，物美价廉，平均一人一两千日元就吃的不错了。我最喜欢里面的辣鸡腿肉，炸鸡套餐和最近新出汉堡肉饼（我也不知道这些餐点的中文翻译是什么，随便说说吧）。汉堡肉饼用了炖牛肉的肉汁，不咸但是鲜香浓郁。而且里面还有自助饮料（Drink Bar），只要花一点钱就可以在店里面慢慢看书看手机无限畅饮，总之是我非常喜欢的店。（有点跑题了，这么写下去就是Gusto的软文了）。

总之，这家店的市场定位吸引到了大量的家庭客户。往往是一家人拖家带口的过来吃饭，而且其定价也可以覆盖收入较低的家庭。这就让它的投放十分精准了。无论是居酒屋还是牛肉盖饭店，都不会有这样精准的客户定位。

可能是我去的家庭餐馆还是少了，我只在Gusto见过这个广告。这是我对Gusto唯一印象不太好的事情。

### 软文轰炸

其次就是互联网上的软文轰炸，或者是SEO优化。我随便给一些软文的链接各位可以自行感受一下。

* [ベストキッズオーディション「誰でも受かる」は本当？合格率・費用・評判を徹底解説2026](https://ikujira.com/40248/)
* [ベストキッズオーディションとは？費用・流れ・口コミ・体験談を全網羅](https://tomotin.com/best-kids-audition/)
* [ベストキッズオーディションの口コミ・評判【費用が金儲けの詐欺？】](https://hetori.com/best-kids-audition-reputation/)

这些软文就是比较基础款的软文，假装独立客观第三方，实则大力宣传，字里行间埋入营销链接。

#### 差评软文

还有这种标题起的像是差评但实际上是软文的东西：

* [【評判怪しい？】ベストキッズオーディションを実際に受けた10人の口コミ結果で後悔するか暴露！](https://newarrivals.xsrv.jp/best-kids-audition/)

之所以会写差评型的软文，是为了SEO污染别人发的差评。至少我在用AI调查的时候，它就几乎成功的污染了我的AI。AI说“建议把它看作一个给孩子留下特殊童年纪念的收费项目来做权衡。”其实这就是所谓差评或者其它部分伪装成中立的软文的“家族の思い出に残る大きなイベントを体験させたい”。

#### 真实体验软文

这种软文的设计就更加精细了，比如这一篇：[ベストキッズオーディション、その後（事務所契約〜活動まで）](https://ikukyudad.com/best-kids-audition-2021-02/)

看上去是真实的体验，事实上也应该是真实存在的孩子的照片和信息，但是我还是在页面的异常种找到了蛛丝马迹。

第一我能看到营销链接植入，**文末带有追踪参数的联盟营销链接**，是明确的营利性目的。这是最让我疑心的地方。

其将活动描述为提升家庭体验的“活动”，对负面信息（如“持续落选”）轻描淡写。强调“不花钱”与“无强制课程”，间接洗白外界质疑的商业模式，呼吁“まずは無料エントリー！！”引导用户报名。

### 谁都能过的二次审查

软文中写一次审查90%都能过，但是二次审查的合格率只有10%。 可是，就我看到的情况是，几乎没有看到有人介绍二次审查失败的心得。 [别人的经验](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12233990437)也大多是“几乎人人都能过二次审查”。 倒是很多人通过二次审查以后收到了入会费和每月的费用的请求[开始犹豫要不要继续](https://ameblo.jp/yukikokomi11091016/entry-12355204746.html)。

所谓的10%，只不过是用软文制造心理落差，给家长一种“二次审查都能通过，我的孩子没准真的能行”的感觉。同时，用“没准二次审查过不了呢，试一试也没什么损失”的心态来降低第一次报名的门槛。

### 看不见的沉没成本与获客漏斗

在二次审查前，家长会花，但是不会花很多钱。最多也就是5000\~10000日元左右（而且应该是可选的）。但是，圈套在免费时已经设下——家长已经投入的时间金钱或许可以忍耐，孩子付出的时间和精神就难以被家长衡量了，很多家长出于为了不让孩子失望，可能会咬着牙交入会费和每月的费用。

更要命的是费用是逐级加码的，为的就是创造一个漏斗，来过滤掉那些付费意愿不够强烈的家庭。这样可以减少客服的数量，降低运营的成本。

只可惜，家长每多交一笔钱，沉没成本就更多，绑定就更加牢固，以后只会收更多的钱。从每月的1万2,到每月12万，等到意识到这是一个无底洞的时候，恐怕也很难抽身了。

## 二次審査通过后还需要多少钱？

按照[这条推文](https://x.com/ri_kun1004/status/932611396788830208)的说法：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d7e062922bce002d74b915cb08cb8d95d2e74281%2F2026-05-05-01-26-53.png?alt=media)

12.8万日元，每月，至少持续10个月，也就是至少128万日元。这哪里是赏金总额100万，这是要倒贴100万啊！

但是我看到更多的信息，10个月总计花费15万左右。

### 二次审查之后提供了什么课程？

比如[这个妈妈](https://qa.mamari.jp/question/16378208) ：

> ### 子供がオーディションに進む中、動画レッスンの料金が高いため辞退しましたが、もったいない気持ちがあります。皆さんの意見をお聞きしたいです。
>
> 子供2人がベストキッズオーディションの2次審査まで受かり、あとはベストキッズアカデミー（登録料26,000円、月謝12980円×9回）を受け、最終審査を受ける予定でした。 しかし、ほとんどが通いのレッスンではなくて、動画提供レッスン（送られて来た動画で自主練）5回だけが通いのレッスンです。 動画で自主練の割に料金が高いとの理由で旦那に反対され辞退することに。 でも、私としてはもったいないことをしたんじゃ…？？？とモヤモヤしてしまっています。 皆様なら、どう思いますか…？ 率直な意見、お願いします。

可以看到，所谓的课程，无非是发一些视频给孩子自己练习，成本几乎为0。就这样的课程的成本居然要十几万日元。

## 孩子真的能成为童星吗？

结论是很难，尽管软文里面说“有名人も輩出”，但是我根本就查不到有哪位童星以前是参加过这个竞赛的。

对于[官网提供的名单](https://audition.photoreco.com/bestkids/kidsmodel/record)，我只能查到几个在NHK的剧里面演过配角级别孩子，而且其演艺的职业道路究竟和这个竞赛有无关系也难以论说。

另外一条路是接商单，且不论没有作为艺人的知名度的孩子能接什么商单，以及日后怎么办暂且不说。我也仅仅看到其宣传声称获奖者将有机会参与“フォトスタジオやアパレルブランドのモデル撮影” (摄影工作室和服装品牌的模特拍摄)，但是这也仅仅是针对获奖者而言。

总之，这家公司的营收肯定不是和童星签约，通过售卖造星课程赚钱，赚的是想要望子成星的家长的钱。而课程本身也不过是发送几个视频，所以称之为欺诈也不算过分。

## 公司的营收大约是多少？

老实说，我也不知道。这家公司又没有上市，也没有公开的会计信息。

按照官方的数据，参赛人数去年已经达到了37万，官方的软文声称只有10%的家庭通过二次审查并且继续付费，年营收也可以达到55亿日元左右（估算），而且还可能有个别家庭能贡献100多万。

但是这样估算是不准确的，这家公司的注册资本也只有1000万日元，官方的参赛人数可能会有水分，即使不考虑水分，按照官方的口径，交费率也到不了10%（审查合格不一定缴费）。直觉告诉我每年也不会有那么多人上当。

我在一则软文里看到，决赛一般有3000个孩子参加，最终会有50个孩子胜出，淘汰倍率大约是60倍。如果这个数据是真的，那么收入大约是3000x15万约合4.5亿日元。

这4.5亿日元我猜能有1千万日元用在举办比赛就不错了，最大的开支应该是人工、广告投放（Gusto等等）、互联网软文营销+SEO。

## 中国有类似的商业模式吗？

当然是有的，中国也存在高度相似的商业模式。如果说日本是打着造星的旗号卖所谓培训班，中国的商业模式运作就更像诈骗了。

这是题外话了，具体的可以看下面的链接：

* [26.8万“造星”幻梦， 一场针对家长与孩子的精密收割](https://szb.fzshb.cn/fzshb/20260303/html/content_20260303003001.htm)
* [起底童星梦背后产业链陷阱：“试戏”变“卖课”，数万元换一场空](https://www.jsjc.gov.cn/yaowen/202512/t20251224_1181053.shtml)
* [数万元换一场空！起底“童星梦”背后的灰色产业链](https://www.chinanews.com.cn/m/sh/2025/12-31/10543398.shtml)


# 互联网政治圈观察日志

## 前言

最近半年来，我仔细观察了主流的中文新闻，社交媒体（Weibo/Twitter/Zhihu/...）的政治相关内容，观测到了很多很有意思的故事和现象。

虽说每个人都是独立的个体，独一无二，但是物以类聚，人以群分，我大致把讨论政治的人群分为如下几类：

* “自由民主”人士（有人蔑称他们为蟑螂，美分，美国的狗，精日，精美，汉奸，叛徒），主要的表现是，对以美国为主导的西方国家无逻辑的信任和支持，存在种族主义，其中相当一部分的中国人对自己中国人的身份感到羞愧，虽然不能确定关联性，但是这部分人中大多信仰基督教。
* 自由民主人士（有人蔑称他们为汉奸，叛徒），他们主要的任务是发表关于中国共产党的黑料，有真有假，但是你能感觉到他们和上面一类人士的明显不同，下面我会举例说明。
* 爱国人士和共产主义者（很少，但是仍然有人蔑称他们为粉蛆），我需要把他们和小粉红分开来，他们中的很多人是真正的爱国人士以及信仰共产主义的，这些人往往不会无逻辑的拥护所有中国政府所支持的言论。相反，因为民主和言论的问题，现在的中国政府是他们批评和打击的对象。
* 小粉红/深红（有人蔑称他们为粉蛆，战狼，支人等等），主要表现是无逻辑的拥护所有中国政府所支持的言论，有着迷之自信；
* 机器人和网络控评员（这个很有意思，它们平时冒充吃瓜群众）
* 吃瓜群众，我算是吃瓜群众的一员，这一类人时不时和别人拌拌嘴，转发点赞感叹一下为主，没有投入任何一方阵营的意愿和勇气；

前面2类在墙内的互联网圈被删了个干干净净，所以基本不存在，偶尔会有类似方方这样的账号出现，也会被内网群嘲。 国内一旦出现前两类甚至第三类，都会被认为是内容失控，从而被约谈甚至关停，所以国内主要是后面几类。而推特之类的境外平台类型是最全面的，可谓什么都有。

## 墙内的政治圈

我并不想过多的讨论墙内的政治内容，因为过于单调和无聊。 国内的审核会Block掉诸如，“自由”，“民主”这样的字眼，也不能讨论国家领导人的名字，甚至代表这些领导人（其实是被网友代表的）的动物和卡通人物也会被屏蔽——我们称之为“和谐”。 或许有人还记得10年前后，连Google搜索“胡萝卜”都不行。

与我们想象的不一样的是，中国政府并没有将手伸入到互联网公司内部——虽然几乎每个公司都有党支部，但是并不是负责审核或思想问题的。他会要求你们根据一份模糊的精神文件自行审核，这份文件就是如雷贯耳，臭名昭著的《互联网信息服务管理办法》。

```
第十五条　互联网信息服务提供者不得制作、复制、发布、传播含有下列内容的信息：
(一)反对宪法所确定的基本原则的；
(二)危害国家安全，泄露国家秘密，颠覆国家政权，破坏国家统一的；
(三)损害国家荣誉和利益的；
(四)煽动民族仇恨、民族歧视，破坏民族团结的；
(五)破坏国家宗教政策，宣扬邪教和封建迷信的；
(六)散布谣言，扰乱社会秩序，破坏社会稳定的；
(七)散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的；
(八)侮辱或者诽谤他人，侵害他人合法权益的；
(九)含有法律、行政法规禁止的其他内容的。
```

比如你很难界定什么是“危害国家安全，泄露国家秘密，颠覆国家政权，破坏国家统一的”内容，或者什么是“损害国家荣誉和利益的”内容，当你说了任何不符合实际情况——哪怕只是一点点差池，也可能是“散布谣言，扰乱社会秩序，破坏社会稳定的”。 最神奇的则是第九条，没人说得清楚什么是“含有法律、行政法规禁止的其他内容”。

而中国政府的网信部发现了让他们觉得不合适的内容怎么办？那就约谈你，如果约谈过后，内容仍然不满意，那就再度约谈，直到他们失去耐心，直接封杀你，使用行政命令停止你的业务。 在这样的高压管理下，许多网站的审核只能做得比网信办更绝，比如知乎就是个中翘楚。网信办的界限是1.0，企业的自我审查就必须做到0.8甚至0.6才能保证企业能顺利地开下去。

当然也不能保证所有的企业都对突发的新闻有足够的觉悟，所以网信办会不定期以非常不正式的方式——比如微信或者电话——通知企业需要屏蔽哪些内容，比如李文亮医生刚去世时候的：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c7d3627713ae1722f41f9b464cc46585f5546547%2F2020-08-23-23-01-34.png?alt=media)

这个已经是比较正式的通知了，而中国每天的一些常见热点事件：上访、强拆、非法拘禁之类的事件很多时候就是一个电话或者是微信就解决了。

在这样的思想下，只能诞生小粉红，他们对政府无条件的信任，而政府及其喉舌媒体则发布大量的西方的黑料。这些小粉红就会非常兴奋的评论，转发，心疼生活在水深火热之中的国外友人。 但是我得说，大部分黑料是真实的，只是片面而已。

但是仍有相当一部分虚假黑料，而且假的相当搞笑，比如：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-cc772afc05264e5ec0b52db5a143306c397e4716%2F2020-08-23-23-10-39.png?alt=media)

估计是为了担心国内的群众模仿，意识到“原来政府是可以被推翻的”，所以捏造了消息，但是在我看来，这不但很恶心，也没有必要。

小粉红对《战狼》这样的影视作品有着相当强的信任和崇拜，觉得自己作为中国公民是受到国家无微不至的呵护的，许多人直到今年民航的5个1出台，才逐渐意识到这就是一部影视作品而已，和《霸道总裁爱上我》一样，都是爽文，而他们，当了真。

小粉红在政治上的幼稚的最大问题，在于总是从既得利益者的角度去考虑问题，而自己却是牺牲品。例如疫情来了，封门，不让出门或者出小区，有人违反规定，辄痛打落水狗，官方惩罚之余，自己还要在微博上口诛笔伐。 但是当自己的家人生病（不是新冠），无法得到救助的时候，又只能可怜的求援。享受了剥削他人自由带来的安全和便利，却未曾想过自己可能的下场。 有人因言获罪，他们认为这是咎由自取：

我曾经用这样一条新闻问了一个小粉红：

> 天津市南开区男子金寿魁，因通过网易跟帖“辱骂党和国家领导人”以及发布“虚假信息”，被判处有期徒刑9个月。

他的评价是：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c83a14f473413f003aaf884392c15346a9c88788%2F2020-08-25-18-38-00.png?alt=media)

他未曾考虑，当有一天他的权益被侵害的时候，很有可能被这些口袋罪把嘴巴堵上，有苦说不出。 这就是比较典型的粉红了。

以上总结：墙内可以认为没有政治圈，只有官方意识形态的洗脑式宣传。

## 墙外的政治圈

墙外的讨论环境要宽松的多，尽管也有对来自中国的小粉红的账号进行批量屏蔽以及引导舆论的行为，但是这个力度和墙内的屏蔽力度比起来就是小巫见大巫，完全不是一个量级的。所以我们基本可以认为墙外的讨论是接近自由的。

### “自由民主”人士

那我们首先介绍和小粉红非常相似，但是针锋相对的一类人群：“自由民主”人士。他们的特点是，无理由，无条件的维护某个西方政府（比如美国政府）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7a91cc698fd0d4ff0060d5ee79c76db910014f64%2F2020-08-23-23-34-19.png?alt=media)

我举出的例子，就是前段时间，曾任美国总统特朗普高级战略顾问的史蒂夫·班农（Steve Bannon）被捕。班农被控为修建美国-墨西哥边境围墙筹款过程中涉嫌欺诈行为。 但是居然有人把这个也说成共产党的阴谋，我也是醉了。美国就这么轻易的被中共搞得翻天覆地，还需要打什么贸易战吗？

除此之外，这些人有不少笃信基督教或者天主教（其实我也不是特别确定教派之类）：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e605ad1183fef856ff089d390f9bb25382b075c6%2F2020-08-25-18-07-22.png?alt=media)

我仔细观察过此类账号，大多是女性，从头像猜测年龄一半在30～50岁，在美国居住，有大量的生活细节，可以确认并不是Robot账户。这些账户的言论大多是： 对美国或者美军的崇拜，以及痛斥中共的邪恶，发一些生活的小确幸，转发一些诸如“村民哄抢翻到大货车”，“大妈排队抢购XX”，“路边有人晕倒无人救助”的负面新闻，并且配上评论，将这些事件的起因归结为中国人的劣根性。

稍微年轻一点的账号会用“支那”或者“支”来描述中国人，例如简称中国人的劣根性为“支性”，创造出“支性难改”之类的词。一些针对政治的讨论往往会演变成针对人种或者国籍的攻击。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7f9cf454e40039766fd89c1277d25b750722c233%2F2020-08-17-17-54-35.png?alt=media)

在他们的眼里：非黑即白，反对我就是不反共，推理可得你就是匪，你被洗脑了，推理可得你个臭粉蛆说什么鬼话还不快滚。

他们的辩论往往是预先划分阵营（二元对立），然后找对方阵营的黑料，也不管这个黑料和对方本人有没有关系，对辩论的目标有没有支持作用。 随后为对方预设立场，把对方划入他们的对立阵营，然后开始用刚才找的黑料攻击。 基于以上奇葩逻辑，一些讨论也会变成互相攻击，以至于你能看到为对方找爹这样的奇观。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-296a753d18aa4ba877a2de2e5a0fff8b8d69a7e5%2F2020-08-17-17-56-16.png?alt=media)

通俗一点地说，就是把你拉到和他一个傻逼水平，然后用它在傻逼领域多年的经验，打败你。

通过这些观察，我的一些总结和不负责任的推论是：这是早期一批对西方有崇拜的人，而互联网的filter bubble，以及白人（WASP）对亚裔的歧视反而加剧了他们的无脑崇拜。 国内一些所谓的Easy Girl其实也是类似的情况，只要是白人，稍微长得像个人样，到亚洲国家一睡能睡好几百个。其实这个也不难理解，百年以前，船坚炮利的西方打开闭关锁国的国门的震撼，以及从此开始的技术落后，让一些人跪下以后再也没有能起来。 被歧视的时候，他们开始找自己的问题，开始对自身作为黄种人感到深深的愧疚和负罪，这也和基督教中的“原罪”理论契合，他们认为这是自己的原罪。（在我看来这是一种非常特殊的种族歧视：对自己的种族歧视进行歧视）

我之所以要和小粉红一起介绍他们，是因为这一类人本质上和小粉红是同一种类型，即：不是用自己对事物的看法决定自己的政治倾向，而是用自己的政治倾向决定自己对事物的看法。稍微粗俗一点的话叫“屁股决定脑袋”（但是这句话目前一般用于描述利益决定行为和观点，其实不是很合适）。

同时大脑结构简单，世界是二元对立的，你不是我的朋友，就是敌方阵营的混账。 小粉红眼里，只有中方和西方，这些“民主人士”眼里，只有中国政府及帮凶，以及反对中国政府的正义之士。

> 不是因为看见才相信，而是因为相信才看见。

这种人比较容易受到宗教乃至邪教的蛊惑，因为信教的一个基本条件就是，你需要无证据的相信存在神。而不需要证据就可以拥有信念的人则是信徒的最好候选者。

这些人里还有一群比较特殊和激进的人，即法轮功，我在国外的街头看到他们捧着传单举着横幅但是当地人不理会，中国来的游客更加不理会，非常寂寞。 他们在网上除了布教，就是痛斥中共邪恶，让大家“三退”。如果说基督教和天主教还有西方多年的神学底蕴撑腰，辩论的时候还可以辩上一辩，忽悠能力更强，那么信法轮功的可能要比基督教和天主教的教徒还要容易“信”。

其实这些人和小粉红一样，是最好管理的“顺民”或者所谓“屁民”，真正的地位，还是要靠打出来的，比如“屋顶上的韩国人”，或者花钱请专业政治公司的印度人。

### 自由民主人士，爱国人士和共产主义者

我也介绍一下真正有政治信仰的人们。

有一些自由民主人士，他们是不满于国内一些黑暗的现状，比如审查，比如暗箱操作的司法，在我看来，其中的佼佼者当属：[中国文字狱事件盘点](https://twitter.com/SpeechFreedomCN/)，这位仁兄举着红旗反红旗，那你自己的手抽你自己的脸，在我看来可谓是很有水平。他的大部分新闻来源都是裁判文书网，一个公开判决书的网站，是有着中国政府背景的网站。在中国还需要“法治社会”这块遮羞布的时候，这个网站应该就不会停止运营。但是在我看来，这个网站属于相当打脸的网站，因为外交部强调中国有言论自由，不会有人因言获罪，但是很多人只是微信上说了一些话，甚至还没有大范围传播，就被抓起来。

言论自由不是可以随便说话，比如我在讨论美术的群里面喊反政府口号，群管理员踢了我，这其实还是言论自由的。 但是如果群管理员到我家来，因为我说的话砸我玻璃，或者拘禁我，这就不是言论自由。

目前的情况属于后者，这里面一大部分属于基层政府滥用公权力，而高院对于这样的案件选择性的不作为的结果。 而因言获罪的案例能够平反的太少，除非这个人很有舆论影响力，说的话也没有那么过分，并且政府无力干涉，才会得到稍微公正一些的判决。

还有一些人是真正的爱国者，和共产主义者，他们对当前中国环境并不满意，做出了和孙文一样的决定：要改变这个中国。 当然这个道路何其艰难，他们的很多做法也称不上聪明或者正确，有些人甚至遭受了牢狱之灾。但是我对真正有理想抱负的人仍然是充满崇敬的。

从推特内容上看，和“自由民主”人士有着相当的重叠，很多都是中国政府相关的负面新闻，区别在于：

他们很清楚造成这些问题的是谁，而不是简单的归咎于人种或者民族性。

### Robot

相比于中国政府道理说不过就关评论，删帖的粗暴行径，日本政府对舆论的引导就显得润物细无声。

在新冠早期，日本为了隐瞒疫情，想强行开奥运会，把检测的规则设置的非常严格，同时官方的态度是日本没有那么多人感染。

但是日本是民主自由的，要是和中国一样删帖，可能民众当时就反了，样子上也过不去，太难看。

这个时候出现了一些推特账号，套路一致，都是：

* “啊，好久没发推特了，春假睡太多了”
* “看老子的蓝莓星冰乐！”
* “仔细想想就知道，哪有那么多人感染啊！谣言真牛逼！”

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3e6e4d17587e5fb16fb50968dcd085993ed4b1ac%2F2020-08-25-19-14-00.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e305458b1eaa93b7edb05cc3854561e609d9012b%2F2020-08-25-19-14-09.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-acb42830b64ad0c047731025393c4f6d18a1f28c%2F2020-08-25-19-14-18.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-cf440b3b120ab6d3a698ea4c14481b2a7ae0cff3%2F2020-08-25-19-14-24.png?alt=media)

之后一个企业家——就是软银老总孙正义——看不下去了，决定给大家提供100万份的PCR检测，结果2个小时内，就被网友骂的收回了他大胆的想法。

> [新型コロナウイルスに不安のある方々に、簡易PCR検査の機会を無償で提供したい。まずは100万人分。申込方法等、これから準備。#コロナ検査有志](https://twitter.com/masason/status/1237670955826069504) [検査したくても検査してもらえない人が多数いると聞いて発案したけど、評判悪いから、やめようかなぁ。。。](https://twitter.com/masason/status/1237703396196413440)

大家的理由是“医疗崩坏”，也就是检测的人多了，再加上试剂可能有假阳性，到医院的人就多了，医院应付不过来，死亡率就会升高。如果我们不检测，大家都不去医院，死亡率就低了…… 我有理由相信，这些“医疗崩坏”，有相当的机器人进行评论，点赞，转发，引导人们相信政府的言论——我们不检测也没事，只要不检测，就没有感染，就没有医疗崩坏。

后来的事情我们都知道了，现在是2020年8月底，日本的第二波疫情还没有看到头。

Robot的事情告诉我们，在民主自由的国家里面，只有保持独立思考，勤加调查，才能有真正的自由，否则只是官方利用的工具和傀儡。

## 总结

这是我一年半载对中文互联网政治圈的观察，从一个推荐系统工程师的角度来看，互联网最终让大家的观点更为过激。 新媒体的出现没有打破小部分人对话语权的垄断，没有包容更加新的思想，反而让大家更容易被引导和利用。

我对这样的互联网，有着深深的遗憾。


# 大学如何喂学生吃屎

仅仅作为一个整理，回忆和实情或有所偏差，且并不只有我的经历，欢迎斧正。

## 手机卡

我不知道诸位何时入学，我入学的时候，伴随录取通知书一同寄到的，就是一张移动的电话卡。彼时我未经世事，不知其中多少关节：为什么是移动的，而不是电信或者联通？里面的套餐有什么玄机？

事实上，大多数的学生拿到学校的卡就不会换了，学校的通知，学院、班级的飞信群，以及我后面说到的上网问题，都让你的还卡代价极大。

更有甚者，据说有些大学给学生办理的套餐，一旦开通就不能轻易解约，如果解约就要支付解约费用。

## 上网

我们的移动卡，绑定了120小时的32MBps（实测从未超过20MBps，下载速度最多2M/秒）宽带。你有可能会疑惑为什么上宽带要还要按照小时计算，事实上，只有在校的大学生"享受"这种待遇，大学对面一街之隔的小区就有便宜的多且不限时，还更快的宽带，但是该宽带的账号并不能在校园使用。

至于为什么是120个小时，按道理说，是学校不想让学生沉迷网络，但是我上大学的时候，智能手机已经开始普及了，每个学生都会用手机刷人人网上QQ，这个理由站不住脚。其实很简单，我们大学四人一个宿舍，4个人的账号轮流登陆路由器，每天抛去熄灯刚好可以大致保持不断网。试想一个学生的账号就够整个宿舍使用了，其它三个人给这个人交份子钱就行了，何苦都办宽带呢？

那么我们如果两个寝室共享网络呢？是不是可以节约一半的成本？我们先不考虑网速，因为大家刷刷网页看看视频大致是够用的。 没问题，运营商早替你想好了，运营商会限制终端数量，这个情况据我所知不止发生在大学。当然，这个可以简单的通过NAT配置破解，让运营商误以为只有一个终端。

更有甚者如电信宽带，我的舍友为了打游戏办过，比学校手机卡自带的套餐速度快，延迟小，但是需要在电脑端安装天翼校园拨号软件才可以上网，为的就是防止你在路由器登录共享网络。后来我了解，这个拨号软件还兼任HTTP劫持，盗取你的流量访问广告网站，自带后门木马等等，真的是一鱼多吃。 顺便一提，宿舍里手机信号是很差的，速度也不快，原因很简单，基站太少，无论哪个运营商都快不起来，基站少不是防辐射，如果速度快大家众筹一个4G路由器就好了，也就不必购买学校价格高昂速度慢的宽带了。

这里说一下我未实现的设想：还是应该搞跨宿舍组网，大家共享网络，账号众筹，根据流量大小自动决定登录PPPoE账号的数量。

## 饭卡、外卖和食堂

大学的食堂都不贵，但是没有说食堂好吃的，以至于网友笑称此乃第九大菜系。一说起食堂我们会想到校长的小舅子，一个和校长亲密又不至于过于亲密的亲戚，承包了这个摇钱树。 事实上，大学不必要通过这样的手段赚钱，学校的食堂是承包出去的，学校只控制：

* 物价不能太高
* 食物不能让学生吃出病

最后根据营业额从中分润，一般食堂的利润率大致在30%左右，也就是10块钱的饭成本是7块，真正的小本生意。学校至少要分去15%，一些黑心的学校甚至要分润20～30%，让食堂大呼做不下去。按说任何商家不得拒收人民币，但是大学的食堂例外，一是为了管理，防止校外人员就餐，二是精确计算食堂收入。

有时校外人员来就餐，就要购买餐券，餐券一定只有中午或者下午在食堂旁边充饭卡的小屋子里有售，食堂绝对无法自己销售餐券，就是要尽可能全面的统计食堂收入。

其实学校的超市，一旦你刷饭卡学校也要从其中抽成，只是不会有食堂那般高昂。

除此之外，饭卡的补办的工本费是30元到50元不等，远超一张射频卡的价格（2元左右），但是这个不作为主要收入。

## 电费和空调

我上大学的时候，大多数的大学是没有空调的，说起宿舍什么最苦，那一定是让人耐不住的严寒和酷暑。北方的大学尚有暖气，南方往往只能缩在被子里瑟瑟发抖。到了夏天，不论南北大家一起流汗。大学里，往往只有留学生公寓有空调，和我们中国人没有关系。

不仅没有空调，也不让你用任何的电热器具，理由是线路老旧。可事实上，一张单人电热毯只有60W，可现在稍微性能强劲一点的笔记本电脑都不止60W了，电热水袋也不过100W。 其实是为了管理方便实行一刀切而已，有的女生得了妇科病煮内裤的电饭锅被没收后，宿管大妈用的很开心，我们寝室之前被没收一个电热水袋，还吃了个单子，后来看见宿管大爷经常使用。 若是真的线路老化不安全，和我们同住一个宿舍楼的大爷大妈怎么能用，就我们不能用呢？建成时间比宿舍楼早的行政楼早就用上空调，空调外机都生锈了，无非学生人微言轻罢了。

但是在15年前后，大学陆陆续续开始改造线路，给学生装空调了，这是为什么呢？两个原因：第一，空调的成本降下来了，家电下乡，空调的成本达到了史上最底，我国是制造大国，宿舍普及空调的成本大幅度下降了。 但是只有成本下降是不会给学生装空调的，哪怕对学生有好处，再小的成本学校也不愿意做，所以第二个原因是，学校发现收电费来钱快。加上第一个理由，学校很快就可以回本了。

我们学校的电费和很多学校一样，是不透明的，你只知道自己用了多少钱，不知道自己用了几度电，账上没钱就断电，你自然知道去交钱。后来有宿舍自己买了功率计测试了，我校大约是1.5～2元一度左右（无法做到精确）。 有的学校不如我校精明，收1.5一度的电费，被学生质疑，学校则解释添置变压器和供电设备都要钱，这就扯蛋了，变压器是供电公司提供的，背后是国家电网。 还有的学校收5毛一度的合理电费，但是悄悄修改学生的使用度数，改得厉害了，甚至出现一个宿舍一个月耗电1000度的奇观。

都不如不告诉学生你用了多少电来的简单，反正学生都是案板上的肉，怎么下刀，学校说了算。

## 其他

以上都是全校范围的盘剥，下面说几个小众的。

机械的学生一般会自学CAD系列的软件，有的老师就会忽悠你们上CAD或者Solidworks之类的培训班，说是有证书，其实这个培训班都是老师亲自开讲，地点也是学校机房。最后需要考试，没有不过的。最后发到手里的证书是没有任何用的证书。

除此之外，还有大学一定要用自己写的教材，但是自己写的教材十分垃圾，我的自动控制原理教材课后习题，第一章7道题有三道是题目有问题的，我和老师确认过后，老师再就几乎不再布置书上的练习。 后来忍无可忍，买了胡寿松的经典教材自学。

这只是冰山一角，实际案例还有更多，比如硕士开题的时候请校外的教授指导，一个人收上几百元——大哥，难道不是和自己的导师讨论，研究，商议吗？ 事实上，两个学校互相聘请教授，互相创收的事情还有很多，至于导师把学生当廉价劳动力用甚至外包（卖）出去，早已是司空见惯。

除了教授，学生会和社团的学生也不是什么善男信女，学生会的老师直接把学生当行政小妹小哥用，还要“值班”，就是帮老师跑腿，和古代的司礼监差不多。一些兴趣类社团更是给新入学的学生兜售设备——比如滑板轮滑鞋羽毛球拍，然后从中赢取利润。不过需要说明，大家虽然不懂，但是还是会淘宝，设备的价格最后还是比淘宝的官方零售价低，所以我也不完全抵触。

## 参考资料

* [多所大学强制新生入学办理指定运营商电话卡](http://finance.sina.com.cn/consume/puguangtai/20100902/20138594564.shtml)
* [大学录取通知书夹电话银行卡 为何屡禁不止？](http://m.xinhuanet.com/edu/2017-08/01/c_129669171.htm)
* [逼学生使用高价校园网的背后：学校年入350万网费](https://zhuanlan.zhihu.com/p/266781234)
  * [附近居民私装信号屏蔽器？燕京理工学院高价校园网后续来了](https://finance.sina.com.cn/tech/2020-10-21/doc-iiznezxr7149401.shtml)
* [哪个学校最坑爹？调查各学校网络服务收费标准](https://bbs.pinggu.org/forum.php?mod=viewthread\&action=printable\&tid=1306719)
* [维基百科 天翼校园](https://zh.wikipedia.org/wiki/%E5%A4%A9%E7%BF%BC%E6%A0%A1%E5%9B%AD)
  * [中国电信天翼客户端被曝挖矿后门：用户电脑变“肉鸡”](https://tech.sina.cn/2017-11-03/detail-ifynmnae1540196.d.html?from=wap)
* [高校“糊涂电费”背后的“糊涂教育”](http://opinion.people.com.cn/n/2014/1104/c1003-25972556.html)
* [广州电信终端限制数到底指什么呢？](https://www.v2ex.com/t/536561)
* [如何看待新生退学生会要写一万字申请？](https://www.zhihu.com/question/354069452)
* [如何评价北京师范大学学十楼宿舍环境恶劣还要执意安装闸机？](https://www.zhihu.com/question/431748925)
* [广东大学最坑爹的宿舍，你学校上榜了么？](https://zhuanlan.zhihu.com/p/39314735)


# 中国网络防火长城简史

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-410ec93c0e68ba8a4d41f17e71d9e459541b7797%2F2020-08-13-00-39-55.png?alt=media)

## 简介

计算机的防火墙，和物理意义上的墙并不一样，物理上的墙建立以后，那就既进不来，也出不去了。而计算机的墙，则是可以对两个方向进行控制。

其实我们今天所说的计算机防火墙，大致有两种，一种是对内的，一种是对外的。

传统意义上的防火墙，一般是用来防止外部访问的，当然，你也可以设置禁止访问某些IP或者网站，禁止内部访问。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3ec85e7f900d84677f98dacf1f0dc85dd857e8ba%2F2020-08-09-16-39-22.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-10715022c4c9af208aa795069720ef7e6aa4f45f%2F2020-08-09-16-46-40.png?alt=media)

我们使用计算机的时候，会开放很多端口，比如22，139，3389等等，而作为非开发者的用户，往往用不到这些端口。 再考虑到使用这些端口的程序有的时候是有漏洞的，开放了容易被攻击，所以干脆打开防火墙封闭了就好。 这就好比你日常宅在家，并不需要出门，就把锁孔都堵死来保证安全一样。

而我们今天讨论的主角，主要的功能不是阻止别人进来（入侵），而是阻止你出去的。 你访问不了Google/Youtube/Facebook/LINE/Twitter/。。。的根本原因： **防火长城，GreatFireWall，简称GFW。**

每当谈起互联网的历史，都会有一种恍如隔世的感觉，当年上网大部分的网站并不需要注册，博客随便看，甚至可以匿名评论。就算要注册，有个邮箱就够了，不需要实名制（实名制在今天的体现是大部分网站注册都要手机号）。大家在网上大体友善的讨论，无私的分享，自由的交谈。几乎所有的网站不需要VPN即可访问。搜索引擎就管搜索，聊天软件也只聊天，没有那么多弹窗和流氓软件。那是互联网的田园时代。

和之前的[互不联网公司是如何强奸用户的(持续更新)](https://github.com/TsingJyujing/blogs/tree/7ddd1d57ad13443ac90c8cc90b23cf62bd14bb9c/spam/spam/rape-user.md)一样，老网虫应该都对这些事情有一些印象，而00后乃至10后可能并不知道这些黑历史。 不论是寿司之神，还是其他顶级的厨师都说过，要想成为好的厨师，首先要吃得出什么是美食。最近辗转反侧，我觉得有必要把这些内容写出来，让人看见，哪怕只是一两个人，告诉大家互联网原来应该是什么样子的，今天为什么是这个样子。

## 墙的历史

1987年，中国发出了第一封电子邮件：“Across the Great Wall, we can reach every corner in the world”（越过长城，走向世界每个角落）。从那一年开始，我们用互联网和这个世界联系在一起了，但是就在12年后，那个越不过去长城，回来了。

1998年，为了防止大家访问部分网站，针对IP和DNS的污染，开始了。伴随着污染，墙和梯的较量正式开始。 早期的GFW不能称之为墙，更像是一个补丁，只是单纯的污染DNS，那么我们修改DNS服务器就可以绕过去。 加上国内的DNS也流氓，所以大家大多会把DNS改成Google提供的 `8.8.8.8/8.8.4.4`。

这样幼稚的屏蔽方式，政府也知道是不行的，于是，真正的GFW正式登上历史舞台。

除了屏蔽特定的IP或者域名之类，GFW还会审查流量内容，因为当时大部分的网站都没有用HTTPS进行加密，所以流量是非常透明的，审查起来很容易。 当然，也会对URL进行审查，有一段时间Google无法访问，就是因为URL里面有一个叫`gs_rfai`的参数，其中“rfa”字样，与在大陆被封锁的自由亚洲电台的网址和英文缩写巧合而被GFW屏蔽。 近几年HTTPS普及了，针对内容审查手段也就慢慢失效了。

墙的其它升级就简单的多了，主要就是两个任务：

1. 针对IP、域名列表的扩充，扩大黑名单范围；
2. 对新增的翻墙工具的封堵。

这些是功能上的升级，性能上也在升级，据传说16年，GFW就拥有100多台小型机做的集群了（而且，各地的GFW似乎也没能完美的同步（也可能是故意的），大致是以省为单位，有的时候会出现某个网站只能被某些省访问的情况出现）。

举些升级的例子：

* 比如你用HTTP/SOCKS代理翻墙，那我就检测代理的流量特征（于是HTTP/SOCKS代理，卒……）。
* 你用SSH翻墙，我就检测你的流量特征，分析你到底是在执行命令，传输文件还是在打隧道翻墙
  * 但是SSH隧道目前仍然可用，技术角度说，完全区分SSH/SFTP和翻墙流量还是有难度的
* 你用GoAgent翻墙，那我就屏蔽Google的所有IP。
* 你用PPTP翻墙，那我就检测PPTP的协议，直接杀。
* OpenVPN也遭遇了PPTP同样的待遇。
* 但是AnyConnect没有，因为很多外企在使用它连接公司内网。
* Shadowsocks也差点遭受和各大VPN一样的待遇，但是因为SS的流量实在难以识别，所以当时（大约2015年）解决的方式更加粗暴：请Shadowsocks的作者喝茶。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-72b69bd1787e99358c1ef8cc4e4ea646a973ef17%2F2020-08-09-17-49-53.png?alt=media)

* 后来（大约2020年前）Shadowsocks还是被识别了，参见[这个报告](https://gfw.report/blog/gfw_shadowsocks/zh.html)。
* 目前最强大的工具应该是V2Ray，除了支持的协议多，其中的VMess协议对计算机时钟的要求让这个系统可以抵御一定的流量重放攻击。
* 最近大家开始用Vultr/Linode搭建服务器……这个的确不好管，但是重要节假日（每年0110月/0100日前后，国庆，两会）就批量屏蔽这些IP。
* GFW不仅防止国内用户访问国外主机，有的时候还会禁止一些境外IP访问国内的主机，这样可以屏蔽部分使用内网穿透技术的人。

## 今天我们怎么翻墙

1. HTTPS代理，TLS给HTTP代理穿上了铠甲，使之又能再战500年。但是考虑到GFW会主动刺探，所以最好将代理的endpoint隐藏在正常网站的某个子路径下。
2. SSH隧道，SSH采用非对称加密，再加上有广泛的正常用途（管理服务器）所以目前SSH隧道还是可用的。
3. Shadowsocks/V2Ray/GoProxy 其实可以理解为对你的代理流量进行加密和混淆的工具，但是GFW在慢慢尝试识别这些流量。
   * 需要特别说明一下的是，GoProxy比较特殊，支持内网穿透。
4. AnyConnect等商用方案。

除此之外，还有内网穿透等一系列骚操作，日后我会补充一些教程，教大家如何搭建自己的翻墙服务。 当然，这一系列教程不能说翻墙，也不能说科学上网，要说连接公司内网，本质上，在公司外部管理公司内网和翻墙的技术基本一致，但是会多一些诸如混淆之类的不必要操作。

## 我对墙的记忆

事实上，我个人没有经历过只有DNS污染这个阶段，我第一次感受到墙的威力，是谷歌退出中国。

山雨欲来风满楼，事实上，早在2007～2009年，就开始出现一些Google搜索的负面新闻：

> 据了解，今年１月和４月，“谷歌中国”网站因存在大量淫秽色情和低俗链接，曾两次被互联网违法和不良信息举报中心公开曝光。近期，举报中心又接到公众举报，经核查发现，“谷歌中国”网站仍然存在大量淫秽色情和低俗信息，包括大量极其低级、丑陋的色情图片、视频和文字。 举报中心有关负责人表示，“谷歌中国”网站未按照我国法律法规的要求做好淫秽色情内容的过滤工作，大量境外互联网上的淫秽色情信息通过该网站传播到我境内，严重违反了《全国人大常委会关于维护互联网安全的决定》和国务院《互联网信息服务管理办法》等法律法规，以及有关行业自律规范，严重侵害青少年身心健康，损害公众利益。 举报中心在强烈谴责“谷歌中国”网站的同时，还建议相关执法部门依法处罚，并希望广大网民继续予以监督。

那么这些色情联想是什么呢：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b9c84c815b622d8724888d03dd7d890ec04e895d%2F2020-08-09-17-58-58.png?alt=media)

但是后来有人发现，这些词以前都没人搜索，是在新闻播出的前些天才有人搜索，搜索量还全部来自于北京。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7083e12385f36bcac03b5b9ba03323437e96d3fc%2F2020-08-09-18-03-58.png?alt=media)

我只能说，你们北京人好BT……当时的说法是，这个事情是百度策划的，的确，深入理解搜索引擎，并且能做出有效的内容攻击，怎么看都不像电视台那帮子文科生能做出来的事情。 只可惜Google的日志留得太全了，被发现了。

很多人说Google退出中国是因为不接受审查，这个是错误的，事实上，Google不是没有配合中国审查，也不只有中国要求Google进行审查。

10年就有人提问，为啥温度计和胡萝卜都不能搜索了[(参见这里)](https://sijiguangchangwc.fang.com/bbs/2811066683~1120/34766730_34766730.htm)。

但是按照规则配合审查是不够的，因为根本就没有什么规则，政府对谷歌（当然对其它所有互联网公司都一样）的要求是**自我审查**。一般来说，还需要配合网信办等神秘机构做一些舆论引导：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4d4d5915afb24c0ff1c31425f1528048c68fe921%2F2020-08-10-21-50-08.png?alt=media)

这些舆论引导并不符合政府一般的办事流程，比如发红头文件，通知，办法之类的。 往往就是个微信消息或者邮件，乃至一个电话，但是不设立专门的部门应对这种不规范的办事方式，公司可能就开不下去。

大致流程就是，你的网站的内容，你自己看着办，我这里只给出精神和模糊的指导意见，但是如果我（政府部门）觉得你内容有问题，那就先约谈，让你再自己琢磨琢磨哪里做错了，要是还是不满意， 我就封鲨你：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-436f8465936f7e6977839b155efc0cad7beff8af%2F2020-08-09-18-04-52.png?alt=media)

简而言之就是：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d2afe0d28e529e0df02ac3b710c396a0a2dfd39c%2F2020-08-09-17-34-38.png?alt=media)

2010年，Google与政府彻底谈崩了，但是还没等Google关闭服务，首先就遭到了DNS污染，这个时候大家通过修改hosts文件还是可以上Google的，那个时候往上大多流传着好多hosts文件的版本。 后来Google就把google.cn做成了静态页面，指向Google香港，但是随着事态恶化，先是用香港Google搜索国家领导人的时候会自动断开，后来香港的Google也很快不能用了。

就是从这里开始，我感受到了墙，也就是从这里开始，百度的搜索质量开始下跌，广告越来越多，越来越隐蔽，广告质量也下降，出现很多虚假广告，比如莆田系的医院。这些都是后话了。

详情可以参考[这篇文章](https://zhuanlan.zhihu.com/p/28620456)，写的相对翔实。

以及这一篇：[深度调查 | 为什么我们不能访问谷歌？(长文慎入)](https://medium.com/@moreless/%E6%B7%B1%E5%BA%A6%E8%B0%83%E6%9F%A5-%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E4%BB%AC%E4%B8%8D%E8%83%BD%E8%AE%BF%E9%97%AE%E8%B0%B7%E6%AD%8C-%E9%95%BF%E6%96%87%E6%85%8E%E5%85%A5-be7f4611780) 写的有理有力，调查清晰，证据翔实，部分稍微有些小错误，但是长文难免有纰漏，瑕不掩瑜，我认为值得一读。

## 墙的设计者

说到墙，不得不讨论一下墙的设计者：方滨兴教授，这个人在中文互联网可谓是臭名昭著。 2011年，方滨兴出现在武汉大学，被学生用鞋子和鸡蛋砸。 同年，方滨兴接受采访的时候说，自己电脑上装了6个VPN。 2013年，方滨兴在微博上发了一条拜年微博，转发评论万余次，大多都是“滚”，评论后被和谐，并被水军洗地。 同年，方滨兴说因为身体原因不再担任北邮校长职务，大家在网络上表示：“祝病魔早日战胜校长”。 搜索引擎上搜索方滨兴，有的时候会联想出来“方滨兴死了吗”。 2016年，方滨兴以杰出校友身份，回到其母校哈尔滨工业大学做题为《定义网络空间安全》的报告。报告中，他试图以韩国政府也架设网络防火墙来论证中国架设网络防火墙的必要性。但由于论证其观点的韩国网页被防火墙阻挡，无法登录，方滨兴只能在众目睽睽之下连接VPN绕过大陆的网络防火墙继续演讲。但连接VPN后，1分钟之内便断线2次，只得用Google搜索网页截屏。由于场面尴尬，报告结束后没有安排任何提问环节。

详细内容可参见[Wiki](https://zh.wikipedia.org/wiki/%E6%96%B9%E6%BB%A8%E5%85%B4)

对于这个人，我只认可他的一句话：中国防火墙与虚拟专用网VPN之间是场“永恒的战争”。

## 后记

我想说：长远来看，墙一定会倒下。但是事实上我看到的是：墙越来越高，越来越厚。 现在的墙是黑名单模式，但是我总感觉在不久的将来，会出现白名单模式。 一旦出现白名单模式，大部分的翻墙手段都会失效，那是我眼中互联网的末日。

敬请期待我之后的文章：**公司内网全攻略**。


# 日本疫情相关记忆

疫情期间我没有回国添乱，也没有去参与那场滑稽的表演。这里记录一下我在日本的Covid19相关的经历和回忆。


# 广岛市开展约80万人的大规模PCR检查

* 新闻原文：[広島市で80万人PCR検査　県、4区の全住民・就業者に](https://www.nikkei.com/article/DGXZQOJB151GD0V10C21A1000000)
* 推特评论：[広島市で大規模PCR検査　80万人対象、県が集中対策](https://twitter.com/nikkei/status/1349899821088313344)

## 对部分评论（简略）的翻译以及吐槽

我从上到下截图，并且按照情绪分类，尽量不漏掉评论。发现负面发言占绝对多数。

日语水平有限，尽力翻译。

### 正面

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b799fd0455e39a5d84273e0bd22b551b256abb0c%2F2021-01-15-18-07-10.png?alt=media)

> 广岛做出了正确判断，期待扩大范围。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8acd7631fd5e08b3199f6ebfde0462ac0855887e%2F2021-01-15-18-11-02.png?alt=media)

> 妙啊！

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-86ffc765d2f5a03e260cc87d3b4e114e9b8c9a0d%2F2021-01-15-18-52-19.png?alt=media)

> 这是很重要的事情。

### 中性和其他

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d606700d19d4bb99f5c6ede83f72ab95de4fbc8a%2F2021-01-15-18-08-54.png?alt=media)

> PCR检查有意义吗？无症状感染者多的情况下，这么多人咋办啊？

（当然是有症状养病没症状隔离啊！难道出去乱跑吗？）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3856aad48f3f3cbb669b13a0b31b30c0c28b9291%2F2021-01-15-18-32-30.png?alt=media)

> 如果对这个（检查）也觉得奇怪的话，是不是承认日本通过限制PCR检测来隐藏（感染者多的）事实。

（其实日本人民就算人人都不承认我们外国人也看得出来）

### 负面

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-68f01e1c00c406a41e4ef1084755967378996f1b%2F2021-01-15-18-10-39.png?alt=media)

> 要乱套。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bfe8a0f5ced75d0cdf68e075a9804e5b6a956b05%2F2021-01-15-18-11-30.png?alt=media)

> 不仅无法抑制感染扩大，反而给医疗体系带来高负荷，作为社会实验很有趣，让我吃个瓜。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bf1ff8d97c35647c00af1cb6ace358d3ad060ccb%2F2021-01-15-18-12-54.png?alt=media)

> 有必要吗？检查出来一大堆阳性怎嘛办？不知道有啥目的……

（一大堆阳性大家自己还不知道是阳性咋办）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-dae39a600533661b79f16bd992e62bd22f1d08de%2F2021-01-15-18-15-55.png?alt=media)

> 完全没意义。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-19adac0805908abff0e09f2f8dc8b9e8774e4d4f%2F2021-01-15-18-16-44.png?alt=media)

> 1～2成感染者不会变成阳性，所以PCR阴性不能保证无感染。

（如果一直放任，倒是可以保证人人有感染）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2c1bc7dfb30bc3a17d04bb0ff6b8c91ee85ae20a%2F2021-01-15-18-34-04.png?alt=media)

> （有点长，懒得准确翻译了，就是还是说假阳性的问题……）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7650650eb4060496d866816b29b7badacf2c5ece%2F2021-01-15-18-36-42.png?alt=media)

> 意思我们明白了，但是今天的日本“检查阳性但是还很精神”的人考虑在内，只是增加（阳性的）数字而已……

（翻译不下去了，啥逻辑啊……）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5e0bc813b7bdbf3be004eb19421a52d7ada68a06%2F2021-01-15-18-41-15.png?alt=media)

> PCR检查了就能治好吗？PCR检查的正确性有80%吗？假阳性患者都得隔离20%，最终以混乱结束吗？

（不能治好，不控制只能保证大家都得病）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-08b592cf3864aa42e6ef726529e5364060126ff4%2F2021-01-15-18-42-36.png?alt=media)

> PCR检查就是诈骗！（下面的翻译略）

（……你说是就是吧）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4a51e6a9536ad7878c6ff1f5a969d33dfda876a4%2F2021-01-15-18-43-13.png?alt=media)

> 预算上拆东墙补西墙的自我安慰行为，只是损害住民的利益罢了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-77d00a15e74bd3471c8175ea9ffbc87f69f42a92%2F2021-01-15-18-44-18.png?alt=media)

> 浪费预算。

（顺便一提，这个人的昵称是：反对强制戴口罩，不要列入指定传染病，感觉是用来引导舆论的账号）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-962d5d2a5d801ea341244678832fd81528aeef1a%2F2021-01-15-18-45-25.png?alt=media)

> 这检查有啥意义吗？

（你说没就没吧……）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4fc9582734347a17fee51b8e39c8779b5126fb9e%2F2021-01-15-18-45-57.png?alt=media)

> 再说，只是想要引人注目的县知事啥都干得出来，世田谷区的模特小姐姐咋弄？

（这是广岛又不是东京）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8513ef8ee3f0966745a4fdc042ef46ed16399518%2F2021-01-15-18-48-38.png?alt=media)

> 傻X吗？

（……）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-1e57e4f589df94ffbb139c0f4501796ea39e5d5d%2F2021-01-15-18-48-53.png?alt=media)

> 让我们整出10万阳性吧！（笑） 真是浪费钱。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ed8c5e8c34c198cf5ce707c80b2239a384472001%2F2021-01-15-18-49-49.png?alt=media)

> 我觉得你这是哪怕知道无差别PCR检测没有意义还是要检测，还是要故意增加阳性患者吗？ 医院怎么办？医疗真的要崩溃了啊！

（大家都不检测的话，是故意增加感染者吗？鸵鸟政策伴随医疗崩溃喊到今年了，医疗真的快崩溃了）

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-837d65e6a3c3f86c1c384ea163de0b683787becb%2F2021-01-15-18-52-37.png?alt=media)

> 扩大PCR检查的英国失败了，中国的感染也在扩大中。应该做的是改变医疗体制，而不是把有限的税金用在无用的检查上，广岛县民又要吃土了。

## 总结

从回复的情况来看，日本网民没有什么长进，和新冠刚开始的时候一样。 政府的鸵鸟政策的确在相当大的程度上代表了民意，不愧是民选的政府。 但是看到这样的评论，说明在科普这件事上，政府或有失职之过。

其实大规模的PCR因为阴性率高，往往30人甚至更多样本混在一起检测，成本其实是很低的。优质的检测技术（ddPCR）也可以减少假阴性。 总之，有“因为用火不安全，所以我们吃生的好了”这样的想法是奇怪的。

出现这样的评论却不是不能理解，毕竟经济受新冠影响很大，餐饮旅游行业怨声载道，但是大家又缺乏不惜一切代价，齐心协力收束事态的勇气，或许是不愿意成为“代价”。 所以，虽然“希望尽早收束事态”是每个人的愿望，但是落实到具体事情，意见就千差万别，尤其是收入受影响的居民。

最后，日推上其实也比较乱，Robot账户、喷子也有不少，并不能完全代表所有日本人，这个只是一个采样而已。


# 推荐系统如何让我们变得极端

## 前言

这个题目我想写很久了，之前我写过一篇[互联网政治圈观察日志](/z-spam/bull-shit-politics)，算是描述了一下现象，我今天想猜测一下起因——以一个推荐系统工程师的身份，猜测和讨论一下推荐系统引起的那些问题。

虽然讨论推荐系统，但是我保证，这是一篇技术无关的文章，不需要任何数学的基础也可以轻松阅读，如果能对你有用，那我就再高兴不过了。文中可能会出现一些政治观点，由于本文不讨论任何政治和立场相关的内容，所有的观点都是为了举例，请务必注意。

文章自认为写的不太好，有些观点可能没有表达清楚，其中一个原因是没找到合适的作图工具，一些图形化表达很简单的观点可能表达的不是那么清晰。

## 我们为什么要有推荐系统

推荐系统的诞生，是为了解决信息过载的问题，今天人类出版书报的速度已经远远超过了单人阅读的极限，更不要说互联网，一个稍微有点人气的论坛，要读完每天的所有新帖子也是一件非常费劲的事情，以至于大一些的论坛需要许多的版主来维护。再比如知乎，每天的新问题，新回答都不是我等能阅读过来的速度，知乎需要雇佣很多人来做内容检查，删掉冲塔的，发广告的，搞黄色的……当然机器学习的NLP技术大大减少了所需要投入的岗位数量，不过这是另一个话题了。

我们从信息匮乏的过载，只用了区区几十年，面对这样过载的信息，必须要有新的技术解决。

首先是分类，比如针对书籍，我们有[中国图书馆分类法](http://www.ztflh.com/)。雅虎早期的内容分类目录也是这一思想的体现。事实上，这一分类法并不少见，很早我们在分类书籍的时候，就分为四大类：经史子集。以及分类生物的时候提出了7个层级：界、门、纲、目、科、属、种。今天打开腾讯的首页，仍然可以看到这些类别：新闻，视频，图片，军事，体育，NBA，娱乐，财经，科技，时尚，汽车，房产，教育，文化，星座，游戏等等。还有一些网站分为了亚洲图片，欧美图片，……咳咳

但是信息仍然在过载，过载到了给每一个物品赋予对应的类别都很麻烦，甚至单单展示分类用的索引或者目录都很困难，要知道，互联网时代不少人的阅读能力是不高于140个字的。这个时候就有搜索引擎了，百度，谷歌，淘宝理论上都是搜索引擎（淘宝早期也是目录式的，现在以推荐+搜索为主了）。但是搜索引擎的基础是，你知道有这个东西，但是不知道在哪里。而我们往往是不知道有这个东西，大家有的时候会发出感叹：淘宝还有这个东西卖啊！就是这样的情况。

所谓的未知有两种：已知的未知，和未知的未知。前者你只是不知道答案，后者，你甚至不知道问题的存在。

这个时候，参考社会化推荐，人类开始准备做推荐系统。推荐系统的一个基础原理是，物以类聚，人以群分，并且将这个原理用数学公式体现出来。 举个例子，我爱读《三体》，但是不知道《流浪地球》，《球形闪电》，这个时候，有一个饱读大刘的朋友给我推荐这些书，那我应该是会欣然接受的。再比如，推荐系统认定我是个科幻迷，于是给我推荐科幻类的书籍，肯定比给我推荐《羊皮卷》这种洗脑书要靠谱的多。

如果我们给M个用户和N个物品都从0开始编号，并且将用户和物品的正面的交互信息（如阅读/点赞/购买）放入矩阵的小格子里，我们可以得到这样一张交互图：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e37510ccb31e615c4b66cd976718d2ee33b0e5c1%2F2020-10-02-16-32-08.png?alt=media)

经过推荐算法处理以后（我这里简单的用了一个SVD代替），大致是这样：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a6353544651ecb1a5d9e920eb47106de354ff3a3%2F2020-10-02-16-33-08.png?alt=media)

我们可以看到人群分为两派，一派喜欢前4种物品，另一派比较喜欢后4种物品，我们就这样把缺失值填上了，看上去问题解决了。 但是新的问题，也就从这里开始。

## 推荐系统为何让我们偏激

相信大家都不是粪车从门口过也要尝尝咸淡的人，所以我们大概率会集中在自己喜欢和支持的东西上——这个看上去也没什么大问题。比如饭圈之前就喜欢搞“封地自萌”，我们小圈子觉得哥哥最帅那就是最帅其实没有任何问题，哪怕出了圈子也没人认识你那哥哥姓甚名谁，你哥哥也可以天下第一。

但是这一切的前提是“人贵自知之明”，你需要知道你看到的结果是由你的历史行为形成的向量召回并且经过精排算法排序形成的，是你自己决定了自己看什么，你所在的就是一个小圈子，万万不要把它当成整个世界。

还是以饭圈为例，你的微博满屏都是你的好哥哥，但并不代表所有的人都在看你的哥哥，对你来说你的好哥哥是97%甚至100%，但是对于整个微博的流量可能只有10%，映射到生活可能只占1%都不到，毕竟大家还要回家做饭收衣服，很忙的！

不幸的是，人类就不是有自知之明的生物，之前的饭圈“破圈”，以及2.27事件就是相当的典型——一个我不认识的私域流量明星的粉丝通过举报等手段，让另一个我不熟悉的圈子（同人圈）的交流和发表作品用的网站消失。 从资本的角度看，所谓破圈是资本是不满足于收割私域流量，准备扩大收割面积，2.27事件是破圈的阵痛，是韭菜对资本无力的抵抗。 但是从普通用户的角度看，是一群异常极端的人在四处攻击他们所有看不顺眼的行为。在我看来，这些异常激动的行为的罪恶根源在于两方：推荐系统及其它原因产生的信息偏置，以及容易被偏置过的信息洗脑的人类。

支持观点A的人看到自己的首页上95%的观点都支持观点A，很高兴，支持观点B的人看到页面上99.85%都支持观点B，更加高兴。在他们看来，只不过还有5%或者0.15%的人似乎反对自己的观点。

**天呐，大家都一边倒的支持自己的观点，怎么还有这么几个人冥顽不灵？**

这个时候人类的另一个缺点——或者从生存的角度说是优点——就起作用了：仗势欺人，大家都觉得自己人多，自己的观点逻辑上也说得通——虽然他们的逻辑可能是基于错误事实推导出来的，但是没关系，我们现在人多，我们一人一口唾沫都能骂到你销号。

所以除了在自己的小圈子里互相转发以外，就是试图去“感化”支持自己能看到的、为数不多的、持对立观点的人。

我认为这种现象可以称作观测偏差，让人产生观测偏差，这也是洗脑的基本原理。一些政府也好、组织也罢，有目的，有组织的对互联网的内容进行注入或者删除，来达到让民众产生观测偏差，进而借此洗脑的目的。 比如我在前一篇文章里提到日本的“蓝莓星冰乐”推特，就是通过注入数据的方式，让大家觉得新冠并不是一个很严重问题，结果被日本网友掀了个底儿掉。 还有一些奇妙的机构，不仅通过注入，还通过删除的方式，让大家产生“岁月静好”，“万众一心”的错觉，就这样还经常翻车，真是“有心栽花花不开”。 这么来看，推荐系统造成的偏置只能算是“无心插柳柳成荫”。

稍微聪明一点的人会用小号或者匿名的模式去单纯的观测，可能可以得到类似：“AB两派势均力敌，但是从数据看，A暂时略胜一筹”这样较为中立的结论。

但是如果真正的从后台数据看，有可能支持观点A和观点B的人加起来不超过30%，大多数人其实支持的是更加温和的观点C，只不过他们甚至都不关心这个问题本身，所以被系统忽略了。 于是我们看到，AB两派吵得天翻地覆，互相骂对方臭不可闻，是狗屎，是蛆，C则在风花雪月，聊天打牌，时不时莫名其妙被卷入AB之争当成对立面里面溅一身口水。

推特上的键政圈里这个问题尤其明显，有些观测偏差甚至不是推特在搞鬼，而是用户自己制造的：

[![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-ac03e3cf5aef49f96d690577b870cba355147d67%2F2020-10-02-16-45-07.png?alt=media)](https://twitter.com/PatrickHsie/status/1243750596605169665)

比如上面这个小工具的主要作用就是：拉黑给某一条推特点赞的用户。 一般来说，有人发现了一条自己极其厌恶的推特，这个时候就可以拉黑所有给这个推特点赞的人。 本身推荐系统就有“协同过滤”之称的算法帮你过滤你不喜欢看的东西了，这个小工具可以说加剧了对立。

对立的产生本身就在于人类不愿意动脑子，不去思索为什么这些人会发让你厌恶的内容，而是单纯的觉得对方恶臭，讨厌，自己不愿意再见到。 我看到一条新闻，不需要确认其真伪，细节，只需要用我脑袋里的政治观点就可以作出评论，参见这个推特下面的评论：[貴州女子高空玩兒秋千](https://twitter.com/niepanxinglai/status/1299162043631558656)。

廖雪峰老师也曾经吐槽过：

\_\_\_\_国交警控制绿灯为过马路老人多亮了97秒，你怎么看？

* A：美国：体现了人类灯塔的光辉；
* B：日本：体现了社会尊老爱幼；
* C：中国：交警滥用职权，行人不守规则，且缺乏天桥等基础设施。

我可以偏激地说一句，使用这个屏蔽点赞用户小工具的人，相当偏激，且相当容易被洗脑。因为他的脑袋里只允许装下一个观点，他的世界只能有一个声音，所有的反对意见都是这个世界的渣滓和污垢，他会认为这样的屏蔽行为只是一种“清洁”，就好像装纱窗防蚊虫一样自然。

## 如何对抗推荐系统

首先，你只要记住：“我看到的信息，大部分都是有偏向性的，不可轻信”，就足以对抗世界上大部分的洗脑，传销，带节奏。你甚至不用去特地对抗，因为你知道信息是有偏的，你就不会偏激。

当然，单单记住这一句话是有点问题的，毕竟我们还要靠互联网收集资料，如果不相信自己看到的信息，意味着无法获取任何有用的信息。这个时候我继续给出一个不是特别完美的解决方案：“孤证不立”。

当我们看到一个观点或者一个信息的时候，在接受它之前，首先最好能找到完整的证据链去证明它。比如你对我推荐系统“物以类聚，人以群分”的观点有疑惑，可以自己造一点数据去跑一跑诸如SVD++这样的算法，就知道我最后两张图表所言不虚。

对于一些证据不好找的内容，可以这样判断：如果你看到一些评论一边倒的内容，只有支持，没有任何反对，那这个内容肯定有问题。

要让一个内容成立，至少找到两个不同的支撑这个内容的证据，如果可以的话，最好找到反对的观点。如果实在无法确认，可以试试自己制造一些反对的观点，看看这些观点会引起怎样的讨论，或者怎样的消失，你对这个内容的可靠性心里就有数了。这样你才可以在相信与不相信之间慢慢抉择，有叫大概率作出正确判断。

所以，你最好关注一些你不喜欢的人，听一听你不喜欢的声音。尽管这么做可能对你造成不适，但是适度的不适可以让你成为一个更加开阔、理性、客观的人。成为所谓客观人不会带给你任何直接收益，但是减少偏激至少有助于防止形成极端主义，例如法西斯那样的独裁和专制。

看到喜欢的或者不喜欢的内容，切勿被一时的情绪所裹挟而冲昏了头脑，仔细想想，这个内容的作者本身可能抱有什么样的目的，作者隐藏了什么没有告诉你的内容。

以本文为例，我前面没有告诉你的是，新颖性也是推荐系统的指标之一，虽然难以评估，但是很重要。 推特并没有太多的过滤你的内容，最多是根据你的关注点赞信息给你推对应的广告，推特也有Trend这样的模块让你发现更大世界。 基于以上观点，将错误全部推给推荐系统并不对，如果你抱着前文的观点跑到互联网公司去要求他们撤掉推荐系统那就不对了，这篇文章也不是为了引起你对推荐系统的仇恨，我还是肯定推荐系统在解决信息爆炸上的价值的。

事实上，推荐系统会造成上述的问题，但是往往小于一些别有用心的人和组织进行的信息注入和删除——这是非常常见引导舆论的手段，这些特性的删除和注入的破坏力也远大于推荐系统造成的因为用户自我选择而带来的倾向性。

希望大家都能有更开阔的视野。


# “救救孩子”

最近发生了两个新闻，一个在中国，一个在美国。中国为了孩子开始对游戏产业开炮，美国为了打击儿童色情要求大家献出自己的隐私。

## “救救孩子”在中国

中国的新闻是《“‘精神鸦片’竟长成数千亿产业”》，其中将游戏称为“电子毒品”，“精神鸦片”。最后定稿的标题是：[《有孩子1天玩8小时网游！精神鸦片竟长成数千亿产业？》](http://sz.people.com.cn/n2/2021/0803/c202846-34850364.html) [（备份链接）](https://archive.vn/0sBHv)其中几个副标题：“网络游戏对未成年人影响触目惊心”，“网络游戏对未成年人影响触目惊心”。 整篇文章基本都在说青少年沉迷网络游戏的问题。

事实上，这不是中国社会第一次讨论游戏的问题，“精神鸦片”是一个相当古老的词汇，在2000～2010年的时候就被频繁提起，与毒品成瘾相似，创造出了一个新词汇“网瘾”，之后滋生了非常多的不正规的“戒网瘾机构”或者“戒网瘾学校”，其中最臭名昭著的两所应该是临沂杨永信的戒网瘾机构和豫章书院，这些机构至今还开着，而且扩展出了更多的业务，比如“帮助孩子矫正性取向”。

这些机构的手段如出一辙，找一些退伍军人，甚至是地痞流氓，对送进来的孩子军队化管理、军训、体罚、感恩教育洗脑、殴打、杨永信的机构还多出一项电击。其实这些机构更像是现代的宗族私刑，而让他们游走在法律边缘（而不是彻底陷入违法）的原因是送进来的大多是未成年人，父母不愿意担负教养的责任，便送孩子去这些机构。

也并不仅仅是游戏，之前中央启动的[“扫黄打非·新风2021”集中行动](http://www.xinhuanet.com/legal/2021-03/20/c_1127233457.htm)，也号称是为了保护未成年人：

> ……筑牢民心工程、高举“护苗”大旗，将以“护苗2021”“净网2021”“秋风2021”专项行动为开展平台，坚持问题导向、强化查处打击，努力实现保护未成年人氛围更加浓厚、网络空间持续清朗、新闻出版传播秩序不断改善的工作目标，为庆祝建党百年、“十四五”开局及开展党史学习教育营造良好的环境氛围。

净网行动直接波及了各种（主要是外国的）动漫和电视剧，非常多的作品（或好或坏）纷纷被下架。最近几年实行的引进动画“先审后播”的机制，枪毙掉了一大堆新番，其中不乏一些佳作。且不论这些作品质量如何，目前并没有任何成文的审核标准，来保证满足这些条件的作品一定能通过审核。所谓的审核标准，大多是诸如：“符合社会主义核心价值观”，“需要宣传正能量” 这些正确、宽泛、易于操作和重新解释的废话。

## 中国真的救了孩子吗？

是，也不是。“是”的部分上面已经说了，下面说说不是的部分。

中国没有作品的年龄分级制度，所以出现了非常滑稽的一幕，《寄生兽》这样有思考有深度的佳作动漫，因为镜头血腥而无法在国内观看，但是莫言的作品中不乏黄色的情节，事实上大量的出现了性爱相关的描写。因为莫言获得了诺贝尔文学奖，所以作品得以豁免审核，即使是小学的孩子也可以买来阅读。

无论是莫言的书还是《寄生兽》，都是佳作，而且都不适合儿童阅读，缺乏年龄分级的结果就是，要么全民读适合低龄的读物，要么就是孩子接触到这个年龄段不该接触的内容。

比如禁止向未成年人出售烟酒，这就是一种年龄分级。烟酒对孩子的危害其实比《寄生兽》大得多，但是妥善的管理让我们仍然可以正常售卖。

为什么我们没有文艺作品的年龄分级呢？

年龄分级需要清晰的、成文的、可执行的标准，如果真的诞生了这个标准，至少会出现两个问题：

1. 人为可操作的空间大大减少，人治变为法制，灰色收入的空间大大减少。
2. 不能再用“救救孩子”这个发臭的幌子对言论自由进行打压，因为已经年龄分级了，符合标准的作品就应该通过。

这两点带来了巨大的阻力，导致从2000年初开始呼吁分级，20年过去了，仍然没有任何变化。

即使从其它角度，我也不觉得真的在救孩子，比如嫖宿幼女罪——不仅多次让对幼女（14岁以下）进行侵害的人免于重刑，还给幼女扣上了“妓女”的帽子，这个恶法2015年才废除。性同意年龄是偏低、偏粗的，也一直没有再细化。

## 美国准备怎么“救救孩子”？

最近美国的苹果公司准备往用户的手机上安装一个[新的功能](https://www.washingtonpost.com/technology/2021/08/05/apple-child-pornography-iphone/)，扫描用户所有的相册照片，提取Hash，和服务器上已有的儿童色情的图片Hash进行比较。 并且苹果声称，不会接受任何来自政府的利用该系统进行监视的请求。

话虽如此，但是：

1. 如果一个技术会被滥用的话，它一定会被滥用，历史上无一例外，这次也不会。
2. （此条也作为1的支撑）苹果并不值得信任，苹果2012年承认参与棱镜门，满足了88%来自政府的数据请求，最近更是爆出2年前SciHub的创始者亚历山大的苹果手机的数据就被苹果公司交给了FBI，在2年后很不正式的发了一封邮件告知此事。

无独有偶，英国在2010年卡梅伦竞选上台的时候，竞选承诺中也包含了打击儿童色情，将为儿童色情专门打造一个系统，从ISP屏蔽这些网站。然而，就在2021年的今天，很明显这个系统被滥用了，[这个列表](https://en.wikipedia.org/wiki/List_of_websites_blocked_in_the_United_Kingdom)远远不止儿童色情了，此后这个系统成了资本维护版权的工具，海盗湾之类的网站接连被屏蔽。今天[连SciHub也被屏蔽了](https://www.ispreview.co.uk/index.php/2021/02/court-order-forces-uk-isp-talktalk-to-block-sci-hub-website.html)。

事实上，美国不是从今天才开始“救救孩子”，FBI一直在监听所有的搜索内容和流量，对涉及儿童色情的进行抓捕，这种行为已经成了梗被网友做成了表情包。

## 美国真的救了孩子吗？

也没有，美国仍然放任毒品，不愿大力管制，除此之外，美国爆出过“萝莉岛”的丑闻，目前主事者已经在狱中“离奇自杀”，案子成了无头案，没有任何证据了。 但是之前爆出疑似涉及的人员名单中，也有喊着“救救孩子”的政客。

所以，美国也一样，“救救孩子”只是一个看上去政治正确的幌子，在这个幌子的遮掩下，大家干着见不得人的事情。

## 孩子真的需要我们这样来救吗？

答案是不需要这样的保护，孩子应该由家长和社会共同抚养教育，长成对社会有所贡献的劳动力，在社会中获得幸福。重点是对教育和保护的投入，这是正面干预，而不是把所有的危险都禁止掉。

例如，如果我们希望保护我们的脚，应该用牛皮做成鞋子穿上，而不是用牛皮把路都包起来。

禁止所有的车上路可以把儿童的交通事故率降为0%，但是正常人都知道这不可取，但是到了文艺作品或者隐私政策的时候，大家又都“糊涂”了。不过我并不觉得政策的制定者是真糊涂，我觉得他们是装糊涂的高手！


# 吃屎系列：资本家如何喂员工吃屎

## 前言

这里记录一些劳资纠纷中，企业如何欺压劳动者的新闻。

## Other Projects

其他一些记录劳动者被欺负的项目：

* [校招污点公司记录](https://github.com/ShameCom/ShameCom)
* [996icu/996.ICU](https://github.com/996icu/996.ICU)
* [fengT-T/996\_list](https://github.com/fengT-T/996_list)
* [623637646/996.Leave](https://github.com/623637646/996.Leave)

## List

List里面的内容只是本人的一个草稿，一些细节未经过考证。

* Amazon
  * 没整理好，比较多
* Facebook
  * 华人员工自杀
* 有赞
  * 明目张胆实施996工作制
    * 接投诉30多起，杭州立案调查有赞公司“996工作制” [「原链接」](https://baijiahao.baidu.com/s?id=1624272494347206177\&wfr=spider) [「存档链接」](https://archive.vn/J9h2P)
* 华为
  * 臭名昭著的奋斗者协议
    * [百度百科：奋斗者协议](https://baike.baidu.com/item/%E5%A5%8B%E6%96%97%E8%80%85%E5%8D%8F%E8%AE%AE) [「存档链接」](https://archive.vn/Z4aOu)
    * [华为《奋斗者协议》被判有效 “家法”熬成“国法”](https://vct.news/news/663aed06-f29e-4b9e-9a4a-35ca2c3b74e6) [「存档链接」](https://archive.vn/auMVq)
    * [起底华为奋斗者协议](https://zhuanlan.zhihu.com/p/269379048) [「存档链接」](https://archive.vn/Itpqk)
  * 华为员工胡玲5千字控诉“研发累死累活，文员却莺歌燕舞” [「原链接」](https://chinadigitaltimes.net/chinese/2019/11/%E5%8D%8E%E4%B8%BA%E5%85%AC%E5%8F%B8-%E8%83%A1%E7%8E%B2%EF%BC%9A%E7%A0%94%E5%8F%91%E5%85%84%E5%BC%9F%E4%BB%AC%E5%AF%B9%E4%B8%8D%E8%B5%B7%EF%BC%8C%E6%88%91%E5%B0%BD%E5%8A%9B%E4%BA%86-%E4%B8%80/) [「存档链接」](https://archive.vn/huGVV)
  * 据红星新闻2日报道，近期，华为裁员的消息引发了各方关注，曾有人在华为内部沟通网站心声社区发帖称“目前华为中国区开始集中清理34岁以上的交付工程维护人员，而研发则是清退40岁以上老员工，主要针对程序员。”但目前，红星新闻记者已无法找到该帖。
  * 员工孕期结束以后，申请回到工作岗位。但是华为要求员工转岗，同时，在原来的部门里除掉了该员工的岗位，并且持续招人。问题来了，既然不需要岗位了，为什么要招人？ 如果需要新人，该员工为什么要被安排调岗？员工被调岗后暂时失业，经过多次申请，终于安排了两个新岗位。但是，新的岗位该员工完全不懂，没有相关经历经验。员工去申请新岗位，被新部门拒绝。自此，华为就终止合同，理由是协商未果。解除了员工的劳动合同，还以“本人意愿”的名义解除，导致员工连失业金都申请不到。
  * 36岁的华为工程师齐智勇，在肯尼亚遭遇过劳死。留下没有工作没有收入、独自带着两个孩子的妻子。 齐智勇从2017年1月到2018年10月，22个月都没有休假回家。即便是节假日，也要为了保障业务全天留在客户处值守。
  * 诬陷自己的员工，最终导致员工被羁押90天
    * [网曝华为前北非经理泄华为内幕 在泰国被中共抓捕](https://ozvoice.org/%E6%96%B0%E9%97%BB/%E4%B8%AD%E5%9B%BD%E8%81%9A%E7%84%A6/%E7%BD%91%E6%9B%9D%E5%8D%8E%E4%B8%BA%E5%89%8D%E5%8C%97%E9%9D%9E%E7%BB%8F%E7%90%86%E6%B3%84%E5%8D%8E%E4%B8%BA%E5%86%85%E5%B9%95-%E5%9C%A8%E6%B3%B0%E5%9B%BD%E8%A2%AB%E4%B8%AD%E5%85%B1%E6%8A%93%E6%8D%95/)
    * [因休假被辞退的华为员工-林夕-的二审上诉状](https://medium.com/@yangzhanqing6121328/%E5%9B%A0%E4%BC%91%E5%81%87%E8%A2%AB%E8%BE%9E%E9%80%80%E7%9A%84%E5%8D%8E%E4%B8%BA%E5%91%98%E5%B7%A5-%E6%9E%97%E5%A4%95-%E7%9A%84%E4%BA%8C%E5%AE%A1%E4%B8%8A%E8%AF%89%E7%8A%B6-bbfe071f0536)
    * [不止李洪元 华为又一前员工被羁押90天](https://finance.sina.cn/chanjing/gsxw/2019-12-02/detail-iihnzhfz3196220.d.html)
    * [专访华为前员工：不止李洪元，我也曾因离职赔偿被羁押90天](https://evil-huawei.github.io/evil-huawei/events/%E5%8D%8E%E4%B8%BA%E5%89%8D%E5%91%98%E5%B7%A5%E6%9E%97%E5%A4%95%E4%BA%8B%E4%BB%B6/)
    * [不止李洪元，我也曾因离职赔偿被羁押90天](https://archive.vn/VK2X5)
  * 诬陷自己的员工，最终导致员工坐牢251天 [华为251事件](https://zh.wikipedia.org/wiki/%E8%8F%AF%E7%82%BA251%E4%BA%8B%E4%BB%B6)
    * 从此华为又被称为“牢厂”
* 网易
  * [网易实行995：强制加班or变相裁员](https://zhuanlan.zhihu.com/p/75830797) [「存档链接」](https://archive.vn/3fxpF)
  * [离职拿N+1有危害？网易的HR和主管是这样劝员工不要拿N+1的](https://thefucking.net/netease_hr_talk/) [「存档链接」](https://archive.vn/P788D)
  * 暴力裁员
    * [人民舆情：网易暴力裁员 回应缺乏诚意 品牌受重创](http://yuqing.people.com.cn/n1/2019/1126/c209043-31474127.html) [「存档链接」](https://archive.vn/q6jhN)
    * [网易裁员 让保安把身患绝症的我赶出公司](https://finance.sina.com.cn/stock/usstock/2019-11-24/doc-iihnzhfz1405328.shtml) [「存档链接」](https://archive.vn/J5K6D)
* 腾讯
  * 暴力裁员
    * [腾讯HR暴力裁员，花式辞退7年老员工](https://thefucking.net/shenzhen_tencent/) [「存档链接」](https://archive.vn/HoYWx)
    * [腾讯暴力裁员？官方回应：该员工严重违反劳动纪律](https://tech.163.com/20/0102/14/F1T2LJGN00097U7R.html) [「存档链接」](https://archive.vn/uS2Od)
* 阿里巴巴
  * 马云声称996是福报
  * [阿里员工求助：两次绩效3.25，开除没有赔偿，我该如何维权？](https://new.qq.com/omn/20200923/20200923A0ENAV00.html)
  * [阿里员工求助：两次绩效3.25，开除没有赔偿，我该如何维权？](https://dy.163.com/article/FN8OTF1905490OET.html)
  * [阿里巴巴月饼门](https://baike.baidu.com/item/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4%E6%9C%88%E9%A5%BC%E9%97%A8)[「存档链接」](https://archive.vn/LH6MW)
    * 这个合理与否或许有争议，但是HR权利过大对于程序员来说不是一个好事情
  * 入职阿里巴巴12年被指“不能胜任工作”，被裁员工获赔近30万
    * 何某某入职阿里巴巴（中国）网络技术有限公司12年，因绩效未达标遭解除合同，杭州市滨江区人民法院日前一审判处阿里巴巴公司赔偿何某某共计近30万元人民币。
  * 阿里员工自述，十年996后被辞退，无赔偿无离职证明。[阿里十年996是个什么结局?](https://zhuanlan.zhihu.com/p/62782492) [「存档链接」](https://archive.vn/XGXgh)

    天眼查披露的判决书显示，2006年3月，何某某入职阿里巴巴公司任客户经理，双方于2014年9月签订了一份无固定期限劳动合同。2018年4月20日，何某某因2018年1月1日至2018年3月31日的绩效成绩为3.25B，出具《绩效改进计划》一份，对其上期（2018年1月1日至2018年3月31日）绩效成绩也为3.25B，属于“不胜任工作"，阿里巴巴公司于2018年8月29向集团工会征求意见，工会亦对解除决定不持异议，决定何某某后工作日为2018年8月31日。此外，何某某拒绝阿里巴巴为其安排的培训，且在2018年4月1日至6月30日间也未达到绩效改进计划中的绩效要求，仍不能胜任工作
  * 有一位阿里员工在某职场论坛晒出了自己被辞退的经历，这位阿里员工今年35岁，有10年开发经验，过完年第一天上班，就被公司劝退，建议他这个月提离职，而被辞退的理由也是让他没想到，公司给的理由是他年纪较大，这位员工也是很迷茫，不知道接下来该如何维持生计。
* 字（xi guan）节跳动
  * “奉劝各位不要去字节跳动了”，怀孕在家的前字节员工控诉老东家持续骚扰。[「原链接」](https://zhuanlan.zhihu.com/p/140737658) [「存档链接」](https://archive.vn/q6jhN)
    * 离职之后，入职字节跳动的“后遗症”开始凸显。首先是一年竞业限制，几乎所有知名企业都不能去；其次是算错税，导致每个月少拿1/3的工资;接着是连续往竞品公司寄钓鱼包裹，让如今怀孕在家的她“听到快递电话就心惊”。最后是联系钓鱼包裹发件人，还遭到辱骂。
* 京东
  * 违法取消快递员底薪，收快递提绩效
  * 4月12日，刘强东在朋友圈发文表示，京东最近四、五年没有实施末位淘汰制了，出现了人员急剧膨胀，发号施令的人愈来愈多，而干活的人愈来愈少的现象。这样下去，京东注定没有希望，公司只会逐渐被市场无情淘汰。他表示，混日子的人不是我的兄弟！真正的兄弟一定是一起拼杀于江湖，一起承担责任和压力，一起享受成功的成果的人！
  * 另外，刘强东还就“996”发表了自己的看法，他表示，京东不会强制员工“996”或“995”，但是，每一个京东人都必须具备拼搏精神。
  * 有传闻称，一名京东被开除员工在公租房中自杀。死者曾经向京东贷款购房，离职时被要求一次性还清。
* 拼多多
  * 好聚，不好散。拼多多竞业协议过于苛刻。
    * [“逃离”拼多多](https://project-gutenberg.github.io/Pincong/post/0039aeeb446a3dd19fe1dd7c03a1ba0d/) [「存档链接」](https://archive.vn/SoADn)
  * 有拼多多员工在脉脉上怒斥拼多多新推出的两班倒值班制度。所谓两班倒，就是早班从早上7点到晚上7点，晚班从晚上7点到第二天早上7点。996已经满足不了拼多多了，必须要27×7地值班才可以。而且这项制度不仅现在要执行，春节要执行，之后每一天都要执行。高层不说停，员工吐血也要干
  * 拼多多员工加班猝死，拼多多在公关时大肆造谣（详情见下）。
* 快手
  * 计时如厕快手厕所装计时器监控员工！ [「原链接」](https://new.qq.com/omn/20201027/20201027A09F5B00.html) [「存档链接」](https://archive.vn/dyxVB)
  * 快手创始人宿华、程一笑2019-6-18联名发布一封全员信,要求996
* 大疆
  * 程序员猝死，大疆称无加班文化，离职员工怒了：有加班考核而已 [「原链接」](http://www.kejilie.com/tmtpost/article/jYBnEb.html) [「存档链接」](https://archive.vn/DVOhi)
* 半木家具要求一名哺乳期女员工每天按一小时600字手写一篇销售心得，按8小时计算，每天需写4800字。
  * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4d59a0d852b3c2e4f8ff5b50cdf98981fd4ac32e%2F2021-01-07-12-39-13.png?alt=media)
* 饿了么
  * 43 岁外卖员送餐时猝死，平台称「不存在劳动关系，只能给人道主义费用」
    1. 为了省成本，不给交社保
    2. 为了不担舆论压力，这风险转嫁给商业保险，而钱是外卖小哥自己交的
    3. 即便如此，外卖小哥的保险钱还是被克扣了！快递员每日需要交3元的服务费，但是只有三分之一左右（1.06元）拿去交了保险，考虑到可能存在虚挂保费的情况，这个比例可能更少。
       * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-97af19c4caa408ac0201dc2144fb02bb8f40763c%2F2021-01-07-12-47-30.png?alt=media)
       * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5967f4d942a72e9ede383b753243f2ec8f1885ec%2F2021-01-07-12-47-39.png?alt=media)
  * 1月11日，47岁的饿了么外卖员刘进因工资被克扣5000元，多次沟通无果后，在配送站前引火自焚。被送往医院后刘进称，“我不想活了，我活够了，太累了。”
* 上海撷果公司（这么小的企业能上List我是没想到的，只是操作实在是太恶心了）
  * 老板赵建民想违法裁员，给人下套开除张宇超，法院不认可，判决赔偿20万。
  * 于是老板把判决书带实名信息[发布到知乎](https://www.zhihu.com/question/435565456)，并且配引导性文字，意图网暴当事人
  * 广大网友不上套，把老板骂了个狗血淋头。
* 滴滴
  * 疑似找借口克扣出租车司机费用引发司机极端言论
  * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a07d6f690e057c88d56f1f944719226d12727b8d%2F2021-01-21-02-23-33.png?alt=media)

## \[2020-12] 上汽乘用车在员工座位下安装传感器

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-43c737ae86b5fff26792376b96091d22858cdfaa%2F2021-01-05-12-36-57.png?alt=media)

有人在知乎上发帖提问，说上汽乘用车在员工座位下安装传感器，名义上是采集数据，但是有人指出早在2014年，上汽就开始使用芯片/指纹/WiFi等等加强考勤了。所以合理的推测，这只是考勤的技术升级。

随后上汽公司官方账号[发布声明](https://www.zhihu.com/question/436828678/answer/1653287465)：

> 关于题主的问题，还是由正主来回答比较好，以免引起大家的误会。首先，我们不会、也不赞同用这样的方式对员工进行管理，这次安装传感器是作数据采集试运行使用，试运行时间预计2周，过后将全部拆除。之所以安装传感器，是因为我们计划对部分办公环境进行共享办公的改造，增加小型会议室和相对开放的办公空间。通过类似停车场指示系统的方式，显示共享办公区域的使用状态，便于大家进行预约使用，这样员工和合作伙伴们无论移动办公或者预约会议室都会更加方便啦！每一名员工都是奋战沙场的勇士，雄鹰振翅九霄，飞跃沧海，理应无所羁绊。

然而其中漏洞很多，首先设备并不便宜，这样的设备，大规模安装，只用两周，感觉是个说不过去的做法。 其次，一般来说运行周期也应该在邮件中说明，但是邮件里只有安装事件，没有运行时间。 更奇怪的是，官方的回复下，出现了奇怪的景象：

根据用户“普利尼日”在01-01的回复：2021.1.1，18:14，目前一共96条回复就被折叠了12条，上汽怕是还没遇到过这种情况的危机公关吧…

也的确有很多人反映评论被折叠了。

同时，出现了很多三无用户或者使用记录很少的用户声援上汽，我只能说十分像水军。

对此，我觉得唯一合理的解释是，本来希望强化考勤，被发布到了网上，引起了讨论，成为了需要危机公关的事情。这个时候，只能编一个拙劣的借口出来糊弄大家，料想自己带节奏大家也不相信，于是公司内外找了一些水军。

## \[2021-01] 拼多多员工猝死事件

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e7b2e013d019e69da28e188170b1235df6a86cc9%2F2021-01-05-12-54-35.png?alt=media)

拼多多有员工猝死了。

员工猝死本身不是特别大的新闻，前段时间商汤也有一个员工猝死，但是大家没说什么，主要还是普及健康安全知识。因为商汤的员工是在公司的健身房锻炼后猝死的，或许也是过劳死，但是这个劳不全是工作压力。

这次猝死的员工特别年轻，1998年出生的一个女生，而且下班时间特别晚，是凌晨一点。这就让同为无产阶级的同志们特别伤心和愤怒。

[作者：匿名用户](https://www.zhihu.com/question/437702180/answer/1658887340)

> 经前同事认证，妹子是凌晨一点半下班路上晕倒猝死的，她男朋友还发了朋友圈，98年的独生女，很漂亮，唱歌很好听，还有同事和她对接过，对她评价很高。说出姑娘的情况，希望每个知道这件事情的人，都可以给姑娘一个祝福和祈祷 人间不值得，希望天堂没有加班，希望姑娘可以开启新的人生。也希望姑娘的家人能够节哀，带着姑娘的希望继续好好生活。同时，拼多多需要给姑娘父母一个交代。我第一时间联系了同在新疆买菜的前同事，对方回复非常官方，说此事件是造谣让大家不要传谣。证实拼多多内部已经启动了保密措施，拼多多，这时候还试图掩盖事实真相，不要以为无法无天没人没机构监管。拼多多用尽手段逼迫员工转岗去买菜，强制辞退不愿去买菜的员工，买菜一线员工全年无休超负荷工作，这些都是事实，真相迟早有被揭开的一天，这些丑陋恶劣的行径都会曝光的阳光下。只是，我特别心痛，丑陋被揭露的同时，竟然是以这样的代价，一个年轻的23岁女孩的鲜活的生命！拼多多的负责人，各级主管，人事负责人，请问你们能够心安吗，午夜梦回，你们是否会为自己的所作所为忏悔，你们能听得到姑娘对这个世界的留恋吗。如果有地狱，你们一定会去那极恶的第十八层！希望姑娘的家人健健康康平平安安好好生活，并通过相关机构维权，拼多多必须给出说法和赔偿，虽然赔偿不能换回姑娘的生命，但这是她曾存在于这个世界的证明。逝者安息，生者坚强！

在这个问题下，拼多多官方回答了问题，语气特别冲，以至于我们怀疑这是潜伏在拼多多内部的友军：

> 你们看看底层的人民，哪一个不是用命换钱，我一直不以为是资本的问题，而是这个社会的问题，这是一个用命拼的时代，你可以选择安逸的日子，但你就要选择安逸带来的后果，人是可以控制自己的努力的，我们都可以

30秒后就删除了回答，但是仍然被手快的网友截图了。

随后拼多多发长文回应员工离世：「我们心痛如绞，我们爱你，深深的想念你」。在回应中，声称大家的截图是造谣，还附上了死者父亲的朋友圈：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-fdaec266481e476cf2d9ccfd092a2ed38a4ec172%2F2021-01-05-13-02-45.png?alt=media)

可惜的是，终究是心急了一些，死者父亲的朋友圈是带“删除”的，而且发布2分钟以内就截图了，说明这个很可能是拼多多公关指使死者父亲发布的。 这还都是小事，晚上18:22，[知乎官方打脸拼多多](https://www.zhihu.com/question/437783708/answer/1660566169)：

> 「拼多多」系知乎注册用户，其身份真实无误。知乎有严格的身份认证流程和机制。 4 日 8 时 19 分 49 秒，「拼多多」创建回答「你们看看底层的人民，哪一个不是用命换钱，我一直不以为是资本的问题，而是这个社会的问题，这是一个用命拼的时代，你可以选择安逸的日子，但你就要选择安逸带来的后果，人是可以控制自己的努力的，我们都可以」。 4 日 8 时 20 分 17 秒，「拼多多」自行删除了上述回答。

我只能说：“精彩！”

随后在30分钟后，拼多多官方把锅扔到了外包的头上：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2f35ecd88a4948b4f50962532c5ca680e5f48942%2F2021-01-05-13-08-26.png?alt=media)

30分钟，就拿到了外包公司员工按着指纹的，和审讯记录一样的道歉信。

到此事情告一段落，但是已经没有人再相信拼多多说的任何一句话，可以说这是相当失败的公关，堪称灾难。

中央政法委长安剑公众号发文，声称把拼多多员工猝死问题泛化为社会的问题是“吃人血馒头”，“大肆表达自己对社会的不满”，是“陷入道德的泥淖”，同时声称，“拼多多也为人带来了‘多多的’快乐”。

详情参见原文：

* [微信原文链接](https://mp.weixin.qq.com/s/ICKGR5yyfhXvD18o2A3Qaw)
* [备份链接](https://archive.vn/s38mo)


# 华为251事件记忆

## [「华为前员工李洪元被羁押 251 天」 事发过程是怎样的？还有哪些值得注意的疑点和信息？ - 王云律师的回答 - 知乎](https://www.zhihu.com/question/359047845/answer/1103111589)

251事件已经过去很久了，估计大家都淡忘了。但是我还是想说，让我们拿出法律，对照事实，那么这件事其实根本没那么复杂。 一、为什么华为的回应这么霸气？ 996的你，还在担心福报和裁员哪个先来？别担心了，牢狱之灾可能比它们来的都早。 在华为251事件之前，只要你还人肥耐操有礼貌，只要你看似光鲜的收入水平除以实际工作时长后得到的平均小时工资还能让老板承受，只要你每年都能交出狼性的KPI，996对你来说还是一种福报，刘强东还会把你当兄弟，丁三石养猪场还是你的家。可是，一旦到了某个时间点，也许是你的头发稀疏到一定程度，也许是HR盘算着你的薪水可以省下来多招三个毕业生，也许是你的身体开始出状况影响到工作节奏，公司的狼性就会转变为兽性，转过头反噬你。到最后，你只能拿着象征性的离职补偿，拖着严重透支的躯体，在人生的下半场重头再来。 但至少，你的人生还有重头再来的可能性。虽然老板们把劳动法当成废纸，虽然HR们做的都不是人事，但起码，他们还没有彻底沦为资本的人格化，还保有一丝人性。 但华为的骚操作，却让你彻底明白什么叫做“阶级矛盾不可调和”。想要更高的离职补偿？来，了解下刑法274条先！想要重头再来？不存在，给你的人生来一场降维打击！ 就算打击失败，华为的回应依然霸气：“不服来告”。因为华为知道，虽然法律面前人人平等，但法律资源面前却是绝对的人人不平等。 根据我国《刑事诉讼法》的相关规定，检察院的不起诉决定，可以分为绝对不起诉、相对不起诉和存疑不起诉三种。绝对不起诉，主要是指检察院经过审查认为根本不存在犯罪事实而不予起诉；相对不起诉，主要是指犯罪嫌疑人犯罪情节显著轻微，检察院经过审查而酌定不予起诉；存疑不起诉，又叫证据不足不起诉，是指检察院经过审查，认为侦查机关提供的证据不足，不符合起诉条件而不予起诉。 李洪元的不起诉决定，是存疑不起诉，也就是说，检察院没有明确下结论说李洪元没有犯罪，而是说现有证据不能证明犯罪事实的存在。所以这就给了华为一个完美的甩锅理由：人是你公安抓的，证据是你公安收集的，逮捕是你检察院批准的，证据不足是你们公检无能，我华为只是报个案而已，报个案有错了？ 华为内有世界顶尖的法务团队，外能聘得起鲜衣怒马的红圈律师，和它所拥有法律资源相比，李洪元有什么？

二、为什么不构成敲诈勒索？ 根据我国《刑法》规定，敲诈勒索罪，是指以非法占有为目的，采用威胁或者要挟的方法，迫使被害人交付财物，达到数额较大或者多次敲诈勒索的行为。笔者认为，认定行为是否构成敲诈勒索罪，主要应当从主、客观两方面加以判断。即行为人主观上具有非法占有他人财物的目的，客观上实施了使用威胁、要挟手段，迫使他人交付财物的行为。 根据根据李洪元的自述，他是2005年10月8日入职华为，第一份合同签到了2006年9月30日。随后又续签了5次合同，每次续签的时间从一年到两年一个月不等，第六次合同到期的时间是2013年12月31日。那次到期后，李洪元和华为没有马上续签，他表示这在华为内部挺常见的，直到2014年1月17日，才和华为第七次签订了劳动合同，约定的合同结束时间是2018年1月31日。这时，华为HR对他进行劝退，提出的离职条件是“N+1”，而李洪元主张“2N”。 根据我国《劳动合同法》第十四条第（三）项规定，用人单位与劳动者连续订立两次固定期限劳动合同后再次续订劳动合同的，应订立无固定期限劳动合同；另根据《最高人民法院关于审理劳动争议适用法律若干问题的解释（一）》第十六条规定，用人单位应当与劳动者订立无固定期限劳动合同而未订立的，视为双方之间存在无固定期限劳动关系。

也就是说，在李洪元与华为第三次续签劳动合同时，本来应该签的是无固定期限劳动合同，但华为仍违法与李洪元订立固定期限合同，那么两者之间实际已存在无固定期限劳动关系。华为在最后一期合同到期后解雇李洪元，则构成违法解除劳动合同，根据《劳动合同法》第八十七条规定，李洪元是可以获得相当于两倍经济补偿金的赔偿金的，也就是其主张的“2N”。换句话说，李洪元主张“2N”，即工作12年应该拿到相当于24个月工资也就是30余万元的补偿，是法定范围内的最高标准，完全合理合法，不存在主观上有非法占有的目的。 三、为什么是251？ 根据我国《刑事诉讼法》规定，公安机关应当在对犯罪嫌疑人采取拘留措施后的三日内提请人民检察院批准逮捕，特殊情况下，提请检察院审查批准的时间可以延长一日到四日；对于流窜作案、多次作案、结伙作案的重大嫌疑分子，公安机关提请审查批准的时间可以延长到三十日；而检察院应当自接到公安机关批捕申请后七日以内作出逮捕或不予逮捕决定。也就是说，公安机关对犯罪嫌疑人采取拘留的期间最长可达3天+4天+30天=37天。李洪元的《不起诉决定书》上载明，其于2018年12月16日被深圳市公安局刑事拘留，经深圳市龙岗区人民检察院批准，于2019年1月22日被深圳市公安局逮捕。2018年12月16日到2019年1月22日，正好37天。 检察院批准逮捕后，公安机关的侦查羁押期间不得超过二个月。李洪元的《不起诉决定书》上载明，移送检察院审查起诉日期为2019年3月21日。2019年1月22日到3月21日，正好二个月，59天。 公安机关移送起诉后，检察院应当在一个月以内决定是否起诉，重大复杂案件可以延长十五日；检察院认为证据不足需要补充侦查的，可以退回公安机关补充侦查，补充侦查应当在一个月以内补充侦查完毕；补充侦查以二次为限。补充侦查完毕移送检察院后，检察院重新计算审查起诉期限。李洪元的《不起诉决定书》上载明，2019年3月21日移送检察院审查起诉，4月19日被第一次退回补充侦查，公安机关在5月17日补查重报，6月14日检察院第二次退回补充侦查，公安机关又在7月12日补查重报，检察院于8月13日延长审查起诉期限，8月22日作出不起诉决定。2019年3月21日至8月22日，用足了两次退回补充侦查和一次延长审查起诉，共155天。 37天+59天+155天=251天，这就是神秘代码251的由来。可以看出，这个案子已经把《刑事诉讼法》规定的各种重大复杂情况下的法定羁押天数几乎都用尽了，可是我们不禁还要问一句：这案子有那么复杂么？ 本答案首发于个人公众号：云律读书

## [兽爷丨大象踩了他一脚](https://mp.weixin.qq.com/s/UaN3WphW0W6sn47L65f8MQ)

2017年9月4日，华为总裁办发出了一封由任正非签署的电子邮件。这封邮件里，一名叫梁山广的员工，因为举报内部创新造假，被任正非点名升职两级，并指派一名高管保证他：

### 免受打击报复。

这封邮件鼓舞了华为上上下下很多人。也是那一年，一个“胖子”，在会议室门口堵住了任正非。

这个人叫李洪元。他也要“清君侧”，反映自己部门的情况。

但任正非告诉他，先找别的领导反映。李洪元就给任正非讲了范睢说秦昭王的典故。在这个典故里，范睢是绕过丞相反映问题的。

这故事很感人，但并没有打动任正非。

越级反映问题的李洪元的命运，与梁山广有着天差之别。

2018年初，李洪元从华为离职。2018年的12月15日，他被深圳警方以涉嫌泄露商业机密拘留。一同进去的，还有4位华为前同事。

华为总部旁的深圳市龙岗看守所，是这家公司重要的新员工教育基地。新员工们排队走过二层，从没有玻璃的天窗俯瞰他们曾经的同事。

有时候，目瞪口呆的新人们走出看守所后，老员工们会露出会心的微笑：

### 习惯就好。

251天后，李洪元走出了这座看守所。如果不是手里的录音存了很多份，这个数字可能会更长。

即使遭此厄运，李洪元对自己的定位自始至终都没变：

### 我对华为有功。

2016年底，李洪元在一封匿名举报信中，向华为高层反映了自己所在的逆变电器业务存在造假问题，虚构合同高达上亿美元。

作为一名在华为工作了11年的员工，李洪元对这个巨大的金额感觉到极度担忧：

### 决定替老板把这个摊子守住。

尽管是匿名举报，在举报信发出之后，李洪元的小主管身份被撸掉了。申请招人被拒，申请借调部门也不批：

### 肯定是暴露了。

参观过龙岗看守所、也经常在华为心声论坛上浏览同事遭遇的李洪元，感觉事情正在起变化。于是他做了一个事后被证明是无比正确的决定：

### 买一支录音笔。

2017年底，李洪元知道了自己合同到期，公司将不再续约的决定。李洪元表示理解公司决定，但把劳动法看了一遍的他，要求公司给予应得补偿。

2018年1月31日下午，李洪元和人力资源部长何承东及HR袁红开始了一场关于离职赔偿的谈判，李洪元按下了录音笔上的红色按钮。

最开始，这是一场亲切友好的谈判。李洪元先是表示自己理解公司决定，愿意离职，还提出了公司绩效评价方式的改进建议。

谈到赔偿金额时，李洪元要求拿到2017年的年终奖，并写入离职赔偿合同。而何承东则表示，这是法务部门规定的文书格式不能改，但自己一定会在年终奖发放时，把钱给李洪元。

谈判的整个过程不时传出笑声，完全没有听到任何威胁。只是在年终奖的发放时间和方式上，有过一个小时的拉扯，两人劝李洪元：

### 你要相信组织。

最终，李洪元选择相信组织。在这份税前补偿41万的离职赔偿合同上，签下了自己的名字。

离职之后的李洪元回老家照顾生病的爷爷。2018年春节刚过，他收到了袁红的通知，要求他必须赶回深圳：

### 领取赔偿和办理手续。

此时，李洪元最初的举报在2018年2月2日换来了十多名该业务高管被通报处罚。

离职手续已经办完了，赔偿不能转账吗？带着疑惑，李洪元赶回了深圳，并于3月8日签署了一份确认书，确认书第二条写到：

### 华为公司委托员工周婷向李洪元支付离职经济补偿款税后共计304742元。

当晚，收到钱的李洪元发现给自己转账的账户是个人账户，他向华为HR热线核实自己的离职赔偿有没有缴税，没有得到答复。之后，他找到了深圳税务局反映情况。

2018年9月，税务局通知李洪元，华为已经补缴了税款。

李洪元没有等来组织口头承诺的年终奖。他打电话给何承东，后者说：

### 你干的那么差，还好意思找我要年终奖。

因为没能拿到年终奖，加之此前谈判时何承东承诺年终奖一定会发放给李洪元，李洪元随即申请劳动仲裁，但这一步并没有成功。2018年11月8日，李洪元向深圳市龙岗区人民法院提起起诉。

2018年12月15日，华为公司委托法务袁鑫，以李洪元伙同他人以泄露商业机密向公司索要离职赔偿为由，向深圳警方报案。

很快，李洪元被警方带走。本应该出席劳动争议起诉的他，被法院做了撤诉处理。

令人不解的是，一同被带走的，还有和李洪元在一个离职员工群的4名华为前同事。

接到逮捕通知书的李洪元被告知，华为向深圳警方报案称，他在2018年1月31日到何承东办公室，威胁给予30万元人民币，否则将会把逆变电器造假的业务公之于众。

华为方面后来跟何承东做了切割。他们官方称这起所谓的敲诈勒索案，是HR何承东为了掩盖业务造假事实，自掏腰包，盗用公司公章，向李洪元支付了赔偿款。

华为在提交给警方的说明函中表示，何承东于2018年3月13日已经被华为方面免除人力资源部长职务，5月31日起，何承东不再担任网络能源产品线的管理人员。

然而根据转账人员周婷的供述，这30万元，有10万是何承东自行转账，20万元是华为部门的工作经费，由何承东使用和支配。

李洪元觉得华为的报案逻辑简直是天大的笑话。

首先，何承东表示自己在1月底被敲诈勒索，然而在2月2日，华为的处罚决定已经将逆变器业务造假公之于众，为什么3月8日还给自己转钱?

第二，如果真的是何承东个人行为，华为为什么为个人行为补缴了税款：

### 你见过被敲诈勒索还去缴税的吗？

但后来，华为向深圳市税务局要回了这笔税款，说是搞错了。

李洪元说，他被捕时，一同带走的，还有那支存放有自己无罪录音的录音笔。出狱后，录音也还在录音笔里。

然而这支录音笔，为何没有成为让李洪元被直接释放的证据，反而需要李洪元再度从后来供职的公司平安保险的电脑里调取录音，成了一个谜。

在被关押整整4个月之后，李洪元见到了妻子给自己雇佣的律师后，说出了憋了4个月的话:

### 我在平安保险工作的时候，谈判录音存在了电脑里。

在妻子和律师的努力下，录音得以上交检察院。

2019年4月19日和5月17日，检察院两次驳回了警方的审查起诉请求，要求再查。

今年7月4日，警方在对何承东的复讯过程中，他突然改了口供：

### 李洪元没有威胁过我。

何承东说，威胁的意思是由主管李鹏转达给自己的。李鹏的证词翻译过来只有三个字：

### 扯犊子。

但这并不妨碍警方第三次申报起诉。由于羁押期已满，检察院最终在今年8月22日做出了不予起诉的决定。

被关押了251天的李洪元，终于走出了龙岗看守所。

11月25日，深圳龙岗区检察院做出决定，赔偿李洪元人身自由损害赔偿金79300元，精神抚恤金27755元，共计107522.94元，并向华为发函，为其消除影响，恢复名誉。

直到现在，李洪元跟我聊时，仍然对任正非使用敬语。重获自由的他，最大的希望是和任正非面谈：

### 期望事后能占用您一个喝咖啡的时间，单独谈谈。

1998年三月，经过多次修正，华为通过了《华为基本法》。其中第二条写着：

### 认真负责和管理有效的员工是华为最大的财富。

梁山广和李洪元都认为自己是认真负责的，签过“奋斗者协议”，自愿放弃年假的华为员工们也都认为自己是认真负责的。

十年前，《新民周刊》报道过华为员工连续身故事件，他们记下了一长串的名字：

### 胡新宇、张立国、李栋兵、乔向英、张锐……

但这并不影响公司内部对任正非的崇拜。即便是李洪元，也坚持认为错在人力和法务：

### 任总身边有坏人。

昨天，任总身边的人没有带来道歉，他们告诉李洪元:

### 我们支持李洪元运用法律武器维护自己的权益,包括起诉华为。

大象踩你一脚，你可以踩回去。

这真是一个公正的回复。

### 李洪元事件时间线

* 2016年11月21日: 李洪元向公司举报逆变电器业务造假
* 2017年年底: 李洪元合约即将到期，主管明确公司将不再续约
* 2018年1月31日: 李洪元同何承东、袁红进行离职谈判，达成一致
* 2018年2月2日: 华为下达了对造假业务高管的处罚决定
* 2018年3月8日: 李洪元赶回深圳，签写确认书，拿到周婷转入的赔偿
* 2018年3月13日: 何承东被免除人力资源部长职务
* 2018年5月31日: 何承东被免除网络能源生产线管理团队成员
* 2018年9月: 深圳市税务局通知李洪元华为补缴了税款，李洪元向何承东去电，没有拿到年终奖，申请劳动仲裁
* 2018年11月: 李洪元向龙岗区人民法院提起起诉
* 2018年12月15日: 华为以泄露商业机密罪报案，李洪元被拘留
* 2018年12月28日: 华为再次补充报案资料，以敲诈勒索罪报案
* 2019年1月22日: 李洪元被正式批捕
* 2019年3月21日: 警方向检察院移交起诉
* 2019年4月: 李洪元见到了代理律师和检察官，表示自己有录音证据，律师拿到了录音
* 2019年4月19日: 检察院第一次退回补查
* 2019年5月17日: 警方再次移交起诉
* 2019年6月14日: 检察院再次退回了警方的起诉请求
* 2019年7月4日: 何承东更改了口供
* 2019年7月12日: 重新移交起诉
* 2019年8月22日: 检察院决定不予起诉，李洪元被释放
* 2019年11月25日: 李洪元获得国家赔偿认定


# 吃屎系列：资本家如何喂用户吃屎

## 前言

“客户就是上帝”——资本说出的话语言犹在耳，而事实却已经是大相径庭。今天的资本，常常借助自己的垄断地位，或者信息的不对称，对用户做了许多下作的事情。 我在此以一个观察者的角度，把资本如何喂大家吃屎的事情记录一下。 也借此希望大家不要忘记，缺乏监管的资本和缺乏监管的政府（公权力）相比，并不温和到哪里去。

## TODO List

以下是尚未来得及整理的一些事情（还有一些不在列表里，是我没想起来）

* 华为（比较多）
  * 存储eMMC和UFS混用事件
    * [华为P10#闪存门](https://zh.wikipedia.org/wiki/%E5%8D%8E%E4%B8%BAP10#%E9%97%AA%E5%AD%98%E9%97%A8)
    * 记者在2017年4月写了批评华为P10手机疏油层和emmc事件的文章，后来4人于10月15日被深圳市龙岗区检察院批准逮捕 [「原链接」](https://www.ciwatch.org/2019/07/20197218.html?m=1) [「存档链接」](https://archive.vn/w2X9b)
  * P40 Pro屏幕混用事件
    * [华为P40Pro混用屏幕再惹争议，为何当年都选京东方今年却都选LG?](https://zhuanlan.zhihu.com/p/123350109) [「存档链接」](https://archive.vn/vHz38)
  * 取消疏油层，报道此事的记者之后被华为报警逮捕。
    * 男子因发布华为Mate20 Pro屏幕泛绿，坐了8个月牢？ [「原链接」](https://kuaibao.qq.com/s/20190722A0UIVA00) [「存档链接」](https://archive.vn/PQDOL)
  * 退换货二次出售
  * 植入后台收集用户数据
  * 百万粉丝数码博主“王员外”（本名王文龙）因5年前（约2020-2021年）发布的4条微博，被华为终端公司报案，最终因“损害商业信誉罪”被二审终审认定有罪，但免予刑事处罚。
    * [王员外案件梳理](https://zhuanlan.zhihu.com/p/1882981738411702124)
      * [王员外案件梳理-备份链接](https://archive.vn/5DK48)
    * [王员外案申诉请求纠正](https://www.sina.cn/news/detail/5285731780265003.html)
      * [王员外案申诉请求纠正-备份链接](https://archive.vn/bTI9i)
* 京东 退换货二次出售（二手东名字来源）
* 西部数据 NAS（红）盘采用SMR技术
* IO DATA 采用廉价盘（疑似SMR）作为NAS存储用，并且定价高昂
* 知乎 故意破坏网页版的功能，引导用户下载App
* 滴滴打车
  * 烧钱补贴占领市场后开始高额收费
  * 大数据杀熟
* 美团
  * 大数据杀熟：[我被美团会员割了韭菜 ](https://mp.weixin.qq.com/s/nsjUyZg-OgrzaUq6Wg-4SA)[「存档链接」](https://archive.vn/Oceut)
* Amazon
  * 虚报预期到货日期
  * 大数据杀熟始作俑者
  * 亚马逊在Covid期间不仅强制要求员工无薪休假，并且如果员工感染冠病，病假是没有薪水发的。
    * [Revealed: Amazon told workers paid sick leave law doesn't cover warehouses](https://www.theguardian.com/technology/2020/may/07/amazon-warehouse-workers-coronavirus-time-off-california)
    * [Amazon to impose new unpaid leave restrictions for warehouse workers starting May 1st](https://www.theverge.com/2020/4/24/21235415/amazon-unpaid-leave-warehouse-workers-covid-19-coronavirus-policy-change)
  * [亚马逊搞了个relief fund让公众捐款](https://www.independent.co.uk/news/world/americas/coronavirus-amazon-jeff-bezos-relief-fund-covid-19-billionaire-net-worth-a9422236.html)
* 携程网
  * 大数据杀熟，两个人订房间，以前经常消费的人定价更高
* 三星
  * 手机爆炸频发，但是打压用户不给赔偿
    * [三星Note7“爆炸门”一年，给了我们什么样的思考？](https://zhuanlan.zhihu.com/p/28371161) [「存档链接」](https://archive.vn/J8AW7)
    * [Wiki: 三星Galaxy\_Note\_7](https://zh.wikipedia.org/wiki/%E4%B8%89%E6%98%9FGalaxy_Note_7)
    * 三星在note7国行爆炸的过程中、过程后，嘴脸都一直令人恶心，先是全世界召回排除国行，后是收走手机污蔑消费者微波炉加热，带动微博工读生引导舆论，实在瞒不住了，被国家约谈后，居然仅仅是先召回部分手机。
* SSD 相关
  * [这些硬盘不要买！2020那些不推荐的固态硬盘](https://zhuanlan.zhihu.com/p/166162142) [「存档链接」](https://archive.vn/hSDBR)
  * 大唐 SSD 虚假宣传，将使用QLC颗粒的固态宣传成TLC
  * 长芯 SSD 虚假宣传，将使用QLC颗粒的固态宣传成TLC，还把低端入门主控宣传为高端主控
  * 海康威视 SSD 主控降级虚假宣传，颗粒黑片回收片混用，QLC冒充TLC，曾被认为是国产固态之光，后来发现和上面都是一路货色。
* (以小米为首的)智能电视给用户频繁推送广告以及开机广告
  * 阿里YunOS强制卸载用户已安装的第三方软件
    * [阿里云yunos盒子自动删除软件怎么办，解决方法！](https://www.znds.com/tv-316684-1-1.html) [「存档链接」](https://archive.vn/WLHU7)
    * [如何评价阿里YunOS强制卸载用户已安装的第三方软件的行为？](https://www.zhihu.com/question/37528010) [「存档链接」](https://archive.vn/4m34k)
* （几乎所有的）打印机厂商
  * 加入芯片强制使用原厂墨水/碳粉
  * 有些时候厂商会刻意让一个产品失效并同时伴随着通知讯息，要求消费者再购买替代品。 例如有些厂商在喷墨打印机的墨水匣里面加上智慧芯片，当用户打印了一定的页数或使用了一定的时间之后，即使墨水匣里面还有墨水（有时甚至还剩下高达五成的墨水），墨水匣便停止作用。 这种状况下的报废并非因为某些零件随机故障，故称为预先设定的报废。
* （以小米为首，包括魅族在内的）大部分国产手机厂商
  * 系统中安装诸如“淘宝”，“苏宁电器”，“京东”，“美团”等等预装软件导致系统膨大。
  * 系统中大部分基础应用（如日历/天气/闹铃/音乐播放器/相册/……）遭到污染，植入大量广告、新闻咨询推送
  * 锁定Bootloader，防止用户刷海外版系统或者其它系统以逃避广告
  * 锁定Bootloader，并且拒绝保修破解Bootloader的手机
* 百度输入法悄悄录音或录像拍照
  * 由于 vivo NEX 为了做全面屏而做了升起式摄像头，以外发现启动百度输入法会触发摄像头升起
  * 百度声称自己是为了输入法做了语音麦克风预热的优化
* 微博的 Android App 疯狂自启并检测用户隐私
  * “一直要自启动也就算了，你特么想偷开我的银行app是几个意思？还有三国杀，你也想来一盘吗？”[「原链接」](https://zhuanlan.zhihu.com/p/113296808) [「存档链接」](https://archive.vn/JiDLX)
* Nvidia为了售卖更贵的Tesla系列，禁止GeForce和TITAN系列显卡（GPU）被用于数据中心里（但可以被用于在数据中心里挖比特币）。
* 联想 同一个型号的电脑，在中国比在国外价格高，用国内消费者的钱补贴外国消费者
  * 联想喜提新绰号：美帝良心想
* Apple
  * [苹果公司相关争议](https://zh.wikipedia.org/wiki/%E8%8B%B9%E6%9E%9C%E5%85%AC%E5%8F%B8%E7%9B%B8%E5%85%B3%E4%BA%89%E8%AE%AE)
  * 臭名昭著的苹果税
    * “苹果税”顾名思义，就是苹果要收的税。当你的APP在APP Store上架，苹果用户下载这个APP如果需要付费，你要交30%的税给苹果。但还有一种情况也要付费，IAP（In-App-Purchase，在App内产生消费）行为抽成。也就是说当苹果用户在APP Store下载的APP里产生消费行为的时候，苹果也会收30%的税。这也是“苹果税”引起争议的地方。
  * 禁止用户获取Root权限——你并不真正拥有自己的手机
  * iPhone 12 （除了法国以外）默认不配充电头（Adapter）和耳机，均需要另外购买
  * 苹果的充电线声称为了环保，使用了容易坏的材质
  * 苹果的充电线需要内置经过MFi认证的芯片，否则无法充电或者通信
  * 苹果强制收集用户应用启动数据
    * 我个人的情况是，如果在路由器上屏蔽 ocsp.apple.com，就无法在用该路由器连接网络的情况下启动任何应用
    * [苹果服务器宕机导致无数应用停止响应](https://www.solidot.org/story?sid=66084) [「存档链接」](https://archive.vn/5tE2I)
    * [Apple is sending a request to their servers for every piece of software you run on your Mac](https://medium.com/@acecilia/apple-is-sending-a-request-to-their-servers-for-every-piece-of-software-you-run-on-your-mac-b0bb509eee65) [「存档链接」](https://archive.vn/ZXCUT)
    * [Does Apple really log every app you run? A technical look](https://blog.jacopo.io/en/post/apple-ocsp/) [「存档链接」](https://archive.vn/jB4uC)
    * 一位网友的实验，苹果甚至在使用不安全的HTTP协议传输用户隐私数据 [「原链接」](https://gist.github.com/TsingJyujing/c5d23c5537383fc264d1075d9b9800ae) [「存档链接」](https://archive.vn/thUcp)
    * [苹果官方的回应，声称为了用户安全](https://support.apple.com/en-us/HT202491)
* 拼多多
  * 病毒+欺骗式营销
  * 销售假货，却还要对外宣称假一赔十，实际消费者维权困难
    * [拼多多的假一赔十是真的吗？](https://www.bilibili.com/read/mobile/7944172) [「存档链接」](https://archive.vn/gAg8i)
    * [拼多多的假一赔十是真的吗？](https://new.qq.com/omn/20201013/20201013A0I4MW00.html) [「存档链接」](https://archive.vn/weciH)
  * 此前，一用户表示，他参与拼多多邀请返现的活动被套路，原本页面显示的是“邀请首次使用新用户1人，直接提现100元”，但该用户截图保存并分享给好友后，显示的却是“随机金额”，不到100元不能提现。而后该网友与拼多多客服沟通，欲提供截图证据，但手机显示“检测拼多多已删除“
    * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-0078ff386cf4170146ad909a829263f2e216bdd7%2F2021-01-13-15-11-31.png?alt=media)
    * ![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b3a6073858b4ab0ca551659f861c7d9fcbde0e27%2F2021-01-13-15-11-57.png?alt=media)
    * [拼多多回应“删除用户照片”：删除的是编辑前的原图，将做产品改进](https://www.guancha.cn/ChanJing/2021_01_12_577679.shtml)
  * 拼多多向用户的手机里塞入后门（内嵌提权代码+动态下发dex）
    * 我个人得说两句：我之前把这件事发在知乎上，阅读量还不算太大，没多少点赞呢，上海寻梦信息技术有限公司不知道是不是搜着关键词就过来了，对着我有证据有道理的回答当场就是一个举报，知乎也是秒怂，立刻就删掉了。敢情拼多多的法务部是不是整天就干这种事儿啊。这点儿气量，这点儿德行，难怪能把法务部干成盈利部门。至于塞没塞后门，大家自己点开下面的链接看吧。我算是累了。
    * [Github链接](https://github.com/davinci1010/pinduoduo_backdoor)
    * [Github备份链接](https://archive.vn/N4ate)
    * [「 深蓝洞察 」2022 年度最“不可赦”漏洞](https://mp.weixin.qq.com/s/P_EYQxOEupqdU0BJMRqWsw)
    * [「 深蓝洞察 」2022 年度最“不可赦”漏洞 -备份链接](https://archive.md/ahCyY)
* 最近制造的拖拉机越来越难以自己修理，中西部农户更加青睐40年前的老拖拉机
  * [「原链接」](https://www.startribune.com/for-tech-weary-midwest-farmers-40-year-old-tractors-now-a-hot-commodity/566737082/) [「存档链接」](https://archive.vn/gySMm)
* 蛋壳公寓（国内其余长租公寓公司大多存在类似现象）
  * [警惕！你租的房子，又要爆雷了 ](https://mp.weixin.qq.com/s/c8_Av3AHvhenvrlXs5CkTA)[「存档链接」](https://archive.vn/EtRuW)
  * 借由疫情，连哄带骗让房东免租，另一头照样收房客的租金，两头吃。
  * 搞房租贷，很多租客，被赶出来之后，得不到任何赔偿，还得继续还租金贷款。
* 单篇文章：[恶龙般的腾讯阿里，让天下没有好做的生意](https://zhuanlan.zhihu.com/p/321639996) [「存档链接」](https://archive.vn/B8M2w)
* Google
  * Google正在封闭Android，要求所有开发者进行注册，否则无法分发应用。用户也将失去在自己的手机上安装任意应用的权限。
    * [Keep Android Open](https://keepandroidopen.org/)
    * 这将让Google拥有封杀自己不喜欢的开发者的权力，同时也有可能配合政府对应用进行封杀。
    * 被封杀的应用可能对自由至关重要——比如VPN，毕竟现在不仅仅只有中国有GFW了。

## 车企开始尝试订阅制/解锁已有硬件来持续薅用户的羊毛

* [为了反击汽车里的内购，车主们开始研究破解版](https://www.huxiu.com/article/624311.html)
  * [backup](https://instant.lvv2.com/html/e0745e9596738b792bd74dda802317c6.html)
* [Tesla to unlock Model 3 heated steering wheel through paid OTA software update soon](https://driveteslacanada.ca/model-3/tesla-unlock-model-3-heated-steering-wheel-soon-paid-ota-software-update/)

### 奔驰推出付费解锁后轮转向服务，每年收费 4998 元

* [奔驰推出付费解锁后轮转向服务，每年收费 4998 元，这一做法是否合理？还有哪些信息值得关注？](https://www.zhihu.com/question/540960588)
* [来搞笑的？！奔驰推出「付费订阅」后轮转向功能：4998 元/年](https://www.iplaysoft.com/news/5732)
* [Mercedes-Benz Offers Yearly Rear Steer Subscription For EQS Sedan In China](https://insideevs.com/news/596076/mercedes-eqs-rear-steer-china-subscription/)
* [Full Rear-Wheel Steering on Mercedes EQS Will Be $575 Annual Subscription in Germany: Report](https://www.thedrive.com/tech/41678/full-rear-wheel-steering-on-mercedes-eqs-will-be-575-annual-subscription-in-germany-report)

## 汽车内屏幕推送广告

* [独家 蔚来回应“导航界面弹广告”：属业务通知推送](https://www.163.com/dy/article/GM73G56I0519C6T9.html)
* [蔚来汽车，请不要学习手机推送广告的那一套](https://news.yiche.com/hao/wenzhang/60180047/)
* [车内屏幕弹“广告”，蔚来有错吗？](https://auto.sina.com.cn/zz/hy/2021-10-15/detail-iktzscyx9706457.shtml)
* [长城哈弗H6中控弹出广告？网友调侃：开车得先冲VIP](https://new.qq.com/rain/a/20210208A0E6O800)
* [哈弗H6启动后中控屏弹广告！网友：看完广告才能挂挡走？](https://finance.sina.com.cn/tech/2021-05-26/doc-ikftpnny5786442.shtml)

## [百度竞价排名事件](https://zh.wikipedia.org/wiki/%E7%99%BE%E5%BA%A6%E7%AB%9E%E4%BB%B7%E6%8E%92%E5%90%8D%E4%BA%8B%E4%BB%B6)

## 美团不退用户改签费用

* 美团：图标变黄了，心变黑了 [「原链接」](https://zhuanlan.zhihu.com/p/279144179) [「存档链接」](https://archive.vn/HU1IF)
* 美团事件后续 [「原链接」](https://zhuanlan.zhihu.com/p/286660082) [「存档链接」](https://archive.vn/lpGlo)

## 因为帮用户获得更好的体验从而惨遭起诉

### 360曾经开发过QQ隐私保护器，引发3Q大战

[「原链接」](https://zh.wikipedia.org/wiki/%E5%A5%87%E8%99%8E360%E4%B8%8E%E8%85%BE%E8%AE%AFQQ%E4%BA%89%E6%96%97%E4%BA%8B%E4%BB%B6#360%E9%9A%90%E7%A7%81%E4%BF%9D%E6%8A%A4%E5%99%A8)

### 独立开发者因为广告拦截被优酷起诉100万元

[「原链接」](https://zhuanlan.zhihu.com/p/285778671) [「存档链接」](https://archive.vn/30Lf7)

### 火狐因为广告屏蔽插件被芒果TV起诉，被判赔偿50万

北京谋智火狐信息技术有限公司与湖南快乐阳光互动娱乐传媒有限公司不正当竞争纠纷二审民事判决书 [「原链接」](http://www.hengdelawyer.com/page95?article_id=66) [「存档链接」](https://archive.vn/eRkKJ)

### PanDownload 作者被捕

PanDownload，于2017年2月19日上线，是一款在百度网盘的基础下提供的第三方百度网盘下载的软件。PanDownload可以在Windows和Android系统上使用，另有Web版，但仅提供通过多线程下载百度网盘文件，及百度网盘分享文件功能。用户可通过该软件以非会员权限突破百度公司对非会员和低级会员用户的恶意限速，而实现百度网盘文件的正常下载。

2020年4月15日，根据“扬州网警巡查执法”官方微博通报，PanDownloadPC端的作者蔡某萌因涉嫌提供侵入、非法控制计算机信息系统的程序、工具罪被逮捕。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5cb767c3b1ddda68d4e4f676a9720392ad09fd74%2Fpandownload-author-arrested-weibo.png?alt=media)

依我看，这就是无稽之谈，欲加之罪而已，平时警方连家暴打伤人都懒得管，居然为了一个用户的举报兴师动众，如果说背后没有百度的PY交易我是不相信的。

而且大家之所以会用这个软件，完全是因为百度做的太贱了（见下文）。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-fd8f096cb02fbbe9a0a2fb432e59acd3409ca8db%2F2020-11-15-01-07-01.png?alt=media)

附知乎对于此事的讨论：<https://www.zhihu.com/question/388025633>

### QQ 机器人作者晨风被警方传唤

知名的IM如Slack，Discord大多都有官方的机器人，Bot其实是IM的刚需，很多通知，系统报警都需要。 QQ自己迟迟没有开放API，也没有开发机器人，于是用户就自己写。 据说最近腾讯准备开放自己的机器人，或许是付费的，我猜测这可能是对作者下杀手的原因。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a192c17a8fd6d1fe7d040bfa86a9e9dbe82b5815%2F2020-11-15-01-20-58.png?alt=media)

[「原链接」](https://my.oschina.net/u/4592167/blog/4473530) [「存档链接」](https://archive.vn/m7kqZ)

## 百度云如何喂用户吃屎

如果你有记忆，百度云最早开始是不限速下载的，于是用户纷纷把资料备份到了百度云上。 一看大家资料多了，离不开了，开始限速下载了，并且要获得不限速的体验，开通会员还不行，需要开通“超级会员”，十分恶心。

而且慢慢的，百度云开始修改用户的数据，最早是和谐了用户存的黄片，后来开始和谐政治视频，医学视频（涉及生殖器），最近已经进化到连《毛选》都不能下载了。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-89077f9d54aa6deca31d7cecb25e3b790817a061%2F2020-11-15-01-18-27.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e50c8d1fcd69c2eedb02657164f6ed8733ef6c79%2F2020-11-15-01-19-18.png?alt=media)


# 互不联网公司是如何强奸用户的(持续更新)

互联网是有记忆的，我会持续收集一些互不联网公司怎么糟践用户的例子，有些可能没有证据，但是上网时间久的老网虫应该都会有印象。

## [微博抄袭小鸡词典案](https://jikipedia.com/hope)

虽然我本人没有任何的证据保留，但是作为有使用过小鸡词典用户，我很清楚的知道微博是如何抄袭小鸡词典的。我没法给出证据也没法帮助小鸡词典，但是我至少要凭着良心写下我知道的事情，而不是让一些大公司的法务像捏小糖果似的拿捏任何小公司或者个人，这对于我一个普通人来说绝非好事。

而且这也不是微博第一次抄袭，微博之前就通过抄袭知乎的方式来营销并且盈利，但是不幸被知乎以投广告的方式钓鱼并且抓到证据：

* [深挖：知乎起诉的微博营销号「大神说」](https://zhuanlan.zhihu.com/p/25377264)
* [知乎起诉微博营销号「知乎大神」，快开庭了！](https://web.archive.org/web/20200815173535/https://zhuanlan.zhihu.com/p/24492978)
  * 可能是因为和小鸡词典一样，“微博还向法庭申请了不公开审理和不公开判决”，所以现在文章只能在互联网备份看到。

> 新浪微博国际版 “有梗百科” 板块，大量抄袭照搬小鸡词典词条。标题，内容，图片，格式，无一不同，抄袭内容多达21278条。

其中微博否认证据，称所有谷歌快照证据，有造假的技术可能性（我笑了），声称所有图片均为伪造。甚至在一审的时候说微博没有网页版。

目前最新的消息是：9月16日，海淀法院一审认为抄袭不存在并驳回了小鸡词典的全部诉讼请求。（我又笑了）

## 360

### upload.360safe.com 事件

2010年的事情

> 金山网络12月31日下午紧急召开发布会，称其安全中心接用户举报，在Google网站上可以搜索到大量中国互联网用户使用互联网的隐私记录，甚至包括用户登陆网站或邮箱的用户名、密码，金山表示已将此事向公安机关报案，并已与上级主管机关汇报。 金山网络表示，经过验证，金山网络发现了一个惊人事实：360在通过其客户端秘密收集用户信息，由于这台服务器的配置问题，导致360不慎将记录了大量用户信息的日志文件被google收录索引，造成大规模外泄。 金山公司在官方微博发一张从google快照中360safe.com服务器上的日志文件，并注明“左侧为360safe.com收集的用户电脑唯一标识码，右侧为该机器访问网络的url记录，其中包括用户名密码、内网传递的资料、用户连续访问多个url的记录，搜索结果记录。”

也就是说，360不仅收集了用户的信息，而且还把用户的信息以HTTP的形式暴露了出来，被搜索引擎的爬虫收集到，最后泄漏在网上。真的是不仅坏，而且蠢。

## 腾讯

1. 早期的QQ会扫描用户的硬盘，但是扫描的原因不详，用途也不详，或许是杀毒，或许是窃取用户隐私。
2. 腾讯QQ有屡禁不止，永远弹不完的弹窗，以至于使用Windows的时候需要安装专门的反弹窗软件（当然这也不止是腾讯的软件这么做）
3. 亲测 QQ TIM 手机版会尝试读取任何NFC能读取的卡，只要卡接触到手机背面，就会跳出来要给你充公交卡，无法关闭，只能禁用NFC

### 腾讯QQ/TIM读取用户浏览器记录证据

* [QQ 正在尝试读取你的浏览记录](https://www.v2ex.com/t/745030)
* [关于QQ读取Chrome历史记录的澄清](https://bbs.pediy.com/thread-265359.htm)
  * [备份链接](https://archive.vn/EH2N8)

[腾讯的官方回应](https://www.zhihu.com/question/439768601/answer/1683913941)声称数据是用于安全用途（为了防止被骂惨居然开了评论审核哈哈哈

> 近日，我们收到外部反馈称PC QQ扫描读取浏览器历史记录。对此，QQ安全团队高度重视并展开调查，发现PC QQ存在读取浏览器历史用以判断用户登录安全风险的情况，读取的数据用于在PC QQ的本地客户端中判断是否恶意登录。所有相关数据不会上传至云端，不会储存，也不会用于任何其他用途。 具体情况为，该操作为历史上线的一个对抗恶意登录的技术解决方案：因系统识别有不少伪造的QQ客户端会恶意访问多个网站作为前期辅助工作，因此在PC QQ客户端中加入了检测恶意和异常的访问逻辑，以此作为辅助手段去判断恶意客户端。 对本次事件，我们深表歉意，内部正梳理历史问题并强化用户数据访问规范。目前，我们已经更换了检测恶意和异常请求的技术逻辑去解决上述安全风险问题，并发布全新的PC QQ版本。为减少不便，所有受影响的PC QQ历史版本将在今天开始进行热更新和推送升级包。同时，手机端QQ不存在上述操作，不受影响。 最后，感谢各位技术爱好者的监督，我们也欢迎大家向腾讯安全应急响应中心（腾讯安全应急响应中心 ）提交报告。

如果说之前的操作是脱用户的裤子，那么这个操作就是把脱了裤子的用户的智商拖在地上锤。 根据用户的逆向分析，腾讯在寻找淘宝、天猫、京东的历史搜索记录，以及寻找“融资”，“古着”等等关键字，分析完成以后会上传分析结果。

如果这个叫检测恶意和异常的访问逻辑，那么我们这些推荐算法工程师都该丢饭碗或者转行做安全了。

### [搜狗输入法疑似通过注入其它程序来突破联网限制](https://www.v2ex.com/t/791087)

备份链接：<https://archive.ph/3ZJO1>

有指出：在 Windows 上，输入法本来就需要注入才能工作，因此这并不是“类似病毒的恶意注入行为”，而是“正常行为，然而防止联网措施难以生效”

## Bilibili

* 网页播放视频只能播放6分钟，逼迫你下载App
  * （2021年初记）最近似乎有所改观
* 国内下载的App和国外下载的App版本居然是不一样的，国外的还不支持直播
  * 这也罢了，这两个版本安装的时候手机都会提醒你这个软件会窃取你的个人信息

## 淘宝

摘自 Google Play

> 祸害人与手机嘛。 所有权限，包括身体活动都要。奇了怪了。1，把京东杀入后台，你怎么总是间歇性去唤醒京东？这与你毫无关系啊？2,几乎手机中9成以上的应用，都间歇性去唤醒！！！你是为啥啊？是帮助手机厂商快点把电池搞坏？是把运行内存使劲搞得不够用？你这样糟蹋手机，你不受益啊。3，一个闲鱼软件庞大到接近535MB,庞大出约400多MB,这是为买卖物品吗？还是为其它？？闲鱼在google pay被下架，是你主动下架，还是被踢出去的。无比丢人和无良，这种拿不要脸当本领的事情，是你一种文化了。 下载过程中的每个节点，一直试图篡改，修改手机系统，手机系统设置。运行中，总是唤醒至少50%的软件，探测很多app软件，包括油管的浏览行踪，交易，通话，信息，如果使用了搜狗输入法，所有发出去的文章，被详细追踪等等使用的详细数据，还有比这恶劣的事情吗？
>
> 太卡、太臃肿！阿里你什么时候能改变？ 华为荣耀V1O手机在其他条件相同的情况下(我一直允许淘宝在后台运行)，自升级到8.9.1后，Android系统持续弹出通知：手机淘宝在后台消耗电量！以前不是这样不会出现这个通知。另外一部荣耀8也出现了相同情况。耗电量为什么比以前更大了呢？

## 爱奇艺

[爱奇艺TV版白嫖用户带宽当PCDN节点 除非强制退出否则就持续白嫖](https://www.landiannews.com/archives/99433.html) [备份链接](https://archive.li/jBIZH)

## 其它

曾记得，2000年初（及以前），软件还没有那么流氓，那个时候的软件是要买光盘（就先甭管盗版正版了）安装的，软件是要钱的，包括杀毒软件。 随着互联网的发展，软件开始可以直接在网上下载了装，免费软件开始大行其道，这个时候自动升级，捆绑安装，诱导安装，弹窗，各种卑劣行径就开始了。

互联网最初的田园时期开始不复存在，逐渐向着今天的互联网环境靠拢，当然，用户侧也作出过努力，系统的权限管理逐渐严格，一定程度上约束了流氓软件，但是仍然没有彻底消灭捆绑安装或者弹窗。

于是，愤青的我留存了证据（截图的时候我还是个学僧，系统还是XP/Win8这样），随后放置了将近10年都没管。 今天整理资料偶然翻到了，我就小刀拉屁股——给您开开眼，看看当年互联网公司是怎么强奸用户的意愿的。这里的勾选框我都是保留最初的缺省选项，没有改动。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-b578ecfe71ebbdf9383db4cf941c9b0042fdab7f%2FBaiduShurufa_2013-10-10_12-54-54.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a61ab7018ed2c422f2247ac8eeb02f03dd9559d2%2FBaiduShurufa_2013-10-16_19-7-29.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7662ba251dba5ab5ac738b80048bce87f1736041%2FBaiduShurufa_2013-10-7_9-44-57.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d68919013122939825cbfc96f56099bf3fe53f4b%2FBaiduShurufa_2013-10-9_19-52-41.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-715ced22ca1f7e0e27dc33a106ed2327d4c07725%2FBaiduShurufa_2013-11-3_20-37-0.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7759a89dfb412dc0212d6ec027a9baf6b541c051%2FBaiduShurufa_2013-10-10_12-55-30.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4a93a05a389a9f4e6a25df26fe57ec35d2538a0d%2FBaiduShurufa_2013-10-18_13-21-36.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-07946fea37cbd9796b02538a3bb87e1d8851a938%2FBaiduShurufa_2013-10-7_9-45-17.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-50e53d177f1393c3b18ca2ac4ba09432ba2c60f8%2FBaiduShurufa_2013-11-10_20-20-8.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7ea4ebe2d783549e73868be2ced1a64b1b787693%2FBaiduShurufa_2013-11-3_20-37-46.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4b9f90ae64a8f937c47540c9e8a13a28d7909bf6%2FBaiduShurufa_2013-10-10_12-56-17.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-cf56aff52315ccf211cd37a53d180edb8ae8c346%2FBaiduShurufa_2013-10-22_21-38-58.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c41992278742f45b94648502e6d647bf167299e1%2FBaiduShurufa_2013-10-7_9-46-7.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a31443848ef571279276fbb6a06172feadf75a27%2FBaiduShurufa_2013-11-3_20-34-20.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e1e1c3b109157e70ba383f59d6626b5f79431978%2FBaiduShurufa_2013-11-3_20-38-20.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-65fc611c678dda6aaadf62eb1fbc4d2d2c747c80%2FBaiduShurufa_2013-10-10_8-4-52.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-15ad0a27b45ef333230204c340229587247acfba%2FBaiduShurufa_2013-10-7_20-34-45.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-c743ac338670456815785b89490660a100c94a54%2FBaiduShurufa_2013-10-7_9-53-41.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2bafb9b0f19f5cdd356552eb0297bf2a2a8e0b7c%2FBaiduShurufa_2013-11-3_20-34-44.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-3322bb18464d8c3b9b33e7b6e2cf745b0ad1019b%2FBaiduShurufa_2014-1-20_10-52-39.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-a871774969011da5529f4079e54d98d6ec803651%2FBaiduShurufa_2013-10-10_8-5-45.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-e2e064d3ebfd313326f219f6ad71e8fbe9947948%2FBaiduShurufa_2013-10-7_9-43-37.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-9400bf3ef5942ce7f91f1c5739cddf1af7ecf483%2FBaiduShurufa_2013-10-7_9-55-6.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-bc853e4c389b89f68072e60330e59178057d461e%2FBaiduShurufa_2013-11-3_20-35-34.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-2d09647bb1949516eee1552595b7fd39720f0833%2FBaiduShurufa_2014-1-20_10-53-26.png?alt=media)


# 《线性代数》（同济版）——教科书中的耻辱柱

## 前言

最近读了[白非立上进记](https://zhuanlan.zhihu.com/p/94398141)，其中线性代数部分真的引起了我相当强烈的共鸣。

我们其实大多数人都是白非立，并不是每个学生都有较强的情报收集能力，或者有大学教授的父母，知道每一门课该怎么学，大家都在等老师来引导，而老师如果引导的乱七八糟，那就学生就只能根据教科书自学，这是最后的救命稻草，如果教科书也一塌糊涂，那就毁了。

其实很多课程，本来每个人都可以学好，结果硬生生毁在教科书上。如果一个人因为学校教科书的不适应而放弃这门课是事故，那么这么多年用同济版或者抄袭同济版的教材的大学可以称之为教学灾难了。 教材的选择错的越离谱，能顺利掌握这门课程的人就越少，到最后只有一些天资聪颖的大神级人物可以学会，这不得不说是学校的失职。

所以我准备吐槽一下这本旷世烂书：《线性代数》同济版。 我想把这本书钉在耻辱柱上，但是耻辱柱说这样会不会太耻辱，后来我还是算了。 我严重怀疑这本书是同济编写了用来拉低其它学校的教学水平的，他们自己可能都未必用。

说到线性代数教材，我上大学的时候用的并不是同济版的线代，那我为什么这么痛恨同济版的教材呢？ 因为我们大学用的是自己编的教材，说是自己编，其实就是抄同济版。具体临床表现为：第一章就讲行列式，知识点支离破碎，结构混乱，和同济版如出一辙。接下来我吐槽同济版的每一个点，基本都可以放到我们大学的教材上。 好在我们老师是明白人，没有完全按照顺序教。 但是书毕竟是买错了，看书复习的时候仍然让我十分痛苦。这样的学习方法，考后也遗忘的十分迅速。直到我遇到了Gilbert Strang，又看了《Linear Algebra Done Right》，重新学了一遍，第二遍让我一直没忘（当然一些证明细节可能糊了）直到今天。

我读大学的时候，可以不从学校手里买书，但是大多数人为了不出幺蛾子，都是按照学校的安排统一买书。我的建议是，别的书还不好说，线性代数就单独摘出来别买了，父母挣点钱不容易，买点擦屁股都太硬的废纸是不是有点浪费？

我本来以为，作为国内一流大学的同济大学，搞出来的教科书水平一定高到不知道哪里去了，至少是不错，就好像我们买东西会认牌子一样。 等我学了一圈别的线性代数再回头看，真是想多了啊，这是我见过的线性代数教材里第一烂的，不是之一，是第一！

下面我们就欣赏一下同济版的线性代数教材。

## 混乱的结构

如果说这本书最大的问题是什么，那就是结构混乱。

第一章，起手就行列式糊你熊脸：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-eea63a5c1541ae628d9c230721ed7e41b1a2b67b%2F2020-08-26-18-42-03.png?alt=media)

兄弟，我们是线性代数课，你不先介绍一下什么叫线性，什么叫代数吗？起手就是n阶行列式的定义，实力劝退。

这里表扬一下我校老师，第一节课会介绍什么叫线性，什么叫代数。

随后就是行列式的计算了：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-10ba38caf19404e5727dbe24439f51772d5474a3%2F2020-08-26-18-44-51.png?alt=media)

啊，原来画对角线就行了啊，那你告诉我，为什么四阶行列式开始不能用这个方法呢？

然后开始讲全排列和逆序数，恍惚中我看到了概率老师在向我招手，一种走错片场恍如隔世的感觉瞬间袭来。可惜当时的我还没有这个感觉，因为我们的概率论安排在线性代数之后。

然后就是对换，转置之类的行列式的性质，以及行列式的……展开，争分夺秒的引入了一个没有什么卵用的……代数余子式。以上介绍伴随着严谨但是佶屈聱牙的证明，再次告诉你：你不是学数学的料，趁早收拾书包滚蛋。

在以上所有的介绍完成以后，终于告诉你，我们的行列式可以用克拉默法则来解线性方程组。无论手算还是计算机算，克拉默法则的计算量比直接消元大到不知道哪里去了，还要考虑全排列和逆序数，我当时已经知道怎么消元，所以我脑子里的第一反应是：脑子坏了才用克拉默法则。

我不知道这个法则在数学史上是什么地位，或者在这门课程里有什么意义——毕竟有些定理虽然直接用不上，但是用来引出概念或者引理之后会变得特别实用。至少克拉默法则在我的学习和工程师生涯中，没有发挥一次作用。后来我还看到一些吐槽，说这个法则解2x2矩阵都不稳定，鬼才用。

伴随着没有卵用的克拉默，第一章，就这样结束了，按照这样的节奏上一节课，脑子里不会有任何框架，概念，只有死记硬背的各种名词和性质。

抱着空空如也的大脑，我们开始了第二、三章的学习：矩阵及其运算+矩阵的初等变换及线性方程组。

就算你搞完这两章，你仍然只是堆积一堆名词概念，比如什么叫矩阵，什么叫秩，但是并不能让你直观的理解他们到底是干什么的。唯一值得“表扬”的是，在第三章的最后，终于犹抱琵琶半遮面的讲到了矩阵和线性方程组的解的关系，虽然这个时候我们已经知道了什么是秩，什么是逆矩阵，甚至已经搞了一把分块矩阵了。但是这个时候我们应该是不知道矩阵和线性方程组有什么关系的。

第四章，开始介绍向量……半本书过去了，矩阵，行列式，那么多复杂的性质介绍完了，矩阵的重要组成部分——向量——终于羞答答的出现在了我们的面前。我倍感欣慰啊，矩阵可以理解为是由向量组成的，BIG NEWS！您要不在第四章介绍，我在看前面的书还真想不到呢，虽然我连秩都应该理解了。哦，这一章又介绍了一遍向量组的秩……我只能说你开心就好。

在这一章的最后，终于开始介绍向量空间，但是通篇介绍还是围绕在向量组的秩，线性有关无关，终于在最后的例题里触及到了向量空间的变换，本书没有大篇幅的讲解空间变换的重要概念，用精炼的仅仅一个例题就教会了大家怎么求两个空间的过渡矩阵。——这也可以理解，最重要的线性空间要在第六章才讲呢，还是选讲。

讲完了向量空间，我们应该说说向量的运算了，所以第五章排的中规中矩，在经历了前四章的磨砺以后，伤痕累累的大脑终于得到了休息。

这一章，叫做“相似矩阵及二次型”。

我有点懵逼，这一本书难道就剩下这俩概念了吗？（让我们先忽略打星号的第六章）

当然不是，因为我们连向量的内积都还没有介绍呐！！！！

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7c514e04120266816c0854efe226cf83f15fa292%2F2020-08-26-21-59-56.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-70d8e7bc4167219135cc5b42bdcad4a45eb76790%2F2020-08-26-22-18-38.png?alt=media)

请注意这个“曾”字，看到这个字，我才知道我误会编者，误会的有多深，这本书，可能还有上册……

总的来说，这一章不止是“相似矩阵及二次型”，包含了向量的一些基本操作，正交基的介绍，特征值与特征向量（这两个衔接得还算自然），相似矩阵，之后才是二次型。

这一章偏应用，内容还算正常，硬要说什么的话，仍然是佶屈聱牙的所谓严谨证明，（对于初学者）炫技一样的例题，以及没有介绍SVD这个工科大杀器。以及，这一章的部分内容完全应该散到其它章节去。

不论如何这已经是最正常的一章节了，就先表扬一下吧！

现在开始打了星号的第六章，选学的第六章，屠龙少年过五关斩六将最后接触到整个线代的核心——线性空间。

WTF？这么重要的内容你现在才讲，这不是坑爹呢吗！整个线性代数的半壁江山（你说全部都可以）就在这线性空间里面，你居然是……选学？选学里不应该放SVD/PCA/雅可比迭代这种乱七八糟的应用类话题吗？你见过把甜点当正餐，然后牛扒或者烤鸡这样的正餐大菜随便啃几口说这是餐后“甜”点的吗？我小时候要是吃冰淇淋吃到吃不下饭是会被家长打的，我不知道谁来把这个编者打一顿。

我看了第六章，我承认，稍微有点向抽象代数的方向偏了，看上去好像数学专业才要学。但是线性空间完全是普通工科学生应该搞懂，可以搞懂的东西。

第六章也不是没有槽点，第六章终于介绍了，矩阵可以作为矢量的线性变换来理解，让我们看看例题吧：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-20bdcbd45b449d11838810a81a0639b607c7bae5%2F2020-08-26-22-17-38.png?alt=media)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-446883fdd6c24e6312f7199f8faea7dafdb5f21e%2F2020-08-26-22-18-10.png?alt=media)

你是觉得大家都已经把抽象代数学完了吗？——抱歉，我忘了，我一个工科狗，是不是不配学这一章。

## 不合理的内容安排

我的吐槽要停止了吗？

没有！

这本书还缺了很多东西，154页的书：

* 没有直观的几何理解，线性变换这种高度依赖几何直觉去理解的东西，居然没图。
* 行列式的几何意义也没有介绍，全是逆序数的炫技，更不要说秩的几何意义了
* 矢量的几何意义不介绍也行，矩阵作为线性变换的几何意义只是轻描淡写用了一个旋转矩阵的例题带过，我记得这个例题的地方还不是在介绍线性变换的地方。
* 二次型的几何意义，以及在优化问题里的工程意义，语焉不详。
* 线性相关和线性无关介绍完以后，没图，没说明
* 下一章讲施密特正交化的时候，有一张靠谱一点的图可以用来说明，让我知道，编书的人并不是手残，只是不想给你画。
* 憋着到了最后一章才讲线性空间，但是这个东西好歹是线性代数的核心，实在是憋不住，四章五节讲了向量空间。

向量，线性方程组，矩阵，以及他们生活的线性空间，不敢说面目全非，至少是被肢解的支离破碎，活生生的要把理科变成文科科目。

这本书有点想向工程实际靠拢，可是实际上，举的例子拙劣不堪，虎头蛇尾，且往往不举实际的例子，就是“我们在XX领域会遇到这样的计算”：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-d38ffb2bf6b562836b71e155c09a19b719407503%2F2020-08-26-22-32-36.png?alt=media)

你这介绍和不介绍有啥区别……凑字数吗？

除此之外，这本书在该下功夫的地方浅尝辄止，在不该下功夫的地方费尽全力，154页当成100页看我觉得都高估了。

除了处处充斥着的“这里不予证明”的嘲讽式定理，还有无图式几何理解：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-f65f0a7daa01256aae85d71028dee9bec5c5a45a%2F2020-08-26-22-40-23.png?alt=media)

## 后记

吐槽完这本旷世烂书，我的心情没有好多少，现在还有多少工科的学生在学习这样一本烂书？他们和白非立一样，没有信息获取的渠道，他们信任的渠道：老师——不一定能给予他们真正的教导。

他们在和烂书缠斗的过程中，逐渐怀疑自己的智商，逐渐厌恶学习，就更不要说学分了，考试能过就阿弥陀佛了。

我不知道我校卖自己编写的教材每年可以增加多少财政收入，但是卖出多少，就可能毁掉很多人对线性代数了解的可能和热爱的可能。在我看来，这是杀鸡取卵，买椟还珠的操作。我们的老师也知道教材有问题，尽力在挽回这个局面，但是，这个局面本来应该如此吗？


# 为什么不应该用国产手机

## 写在前面

这里的国产手机泛指未经Google认证的国产安卓系统手机，并不是指所有Made in China的手机。 事实上，Made in China很有竞争力，有一家厂商推出了一款手机，同时提供纯美国制造的版本，纯美国制造的版本要比世界协作的版本贵一倍：

[![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-7add3fda4e8d4306bd72e4dd89a9a5c443867b2f%2F2021-05-26-12-41-34.png?alt=media)](https://puri.sm/posts/librem-5-and-librem-5-usa-what-are-the-differences/)

我今天要说的主要是在中国开发的操作系统，有何问题我会稍后一一列出。

在此之前，可能会存在这样的疑问：**“如果软件有问题，那就刷机换系统不行吗？”**

当然是可以的，但是手机和电脑不一样，硬件是高度定制的，对应的固件和驱动程序（HAL/硬件抽象层）大多是私有的，这就决定了你不能随意的刷机。而且如果刷机换系统，甚至只是获取Root权限，你的保修可能就没了。

虽说存在诸多的限制，但是大家仍然有有限的自由：

1. 论坛的大神会放出各种破解版，修改版等等
2. 同一系列的手机不止在国内销售，在国外销售的系统会使用Google认证的原生Android。
3. 有一些厂商会开源底层的固件驱动，当然一般是旧型号的手机

但是国内厂商并不乐意你这么做，以笔者以前用过的魅族15为例，魅族15的国内国外销售的手机硬件完全一样，我出国以后希望刷Google原生系统，但是失败了，起初我以为是硬件上的问题，直到我看到了另一个刷机教程才知道我肤浅了，魅族往手机的ROM的某一处写了一个ID，这个ID可以用来区别这是在国内还是在国外销售的手机，由此决定手机是否可以刷国际版ROM。

想要用上国际版ROM，先得获取Root权限，执行一个脚本修改这个ID才行。

[刷机教程在这里](https://www.bokezhu.com/2020/02/27/632.html)，但是这不是我当时看的一篇，现在魅族论坛上所有关于国际版ROM的讨论都被魅族官方和谐了，可见魅族官方并不希望大家刷国际版ROM。

为什么魅族官方这么不想大家刷国际版ROM呢？因为用国产的ROM对魅族更加有利。国际版的ROM需要过Google的认证，除了国内有GFW，导致Google的原生程序使用不便以外，厂商不能肆意的往系统里塞东西，这里的塞东西是中性的，既指为了用户体验而定制的良好设计，也指广告程序，用户数据收集和各种内置后门。

## 广告多

广告和推送多，是不想用中国版Android系统的第一理由。 我自己以前统计过，魅族有11处广告，而且没法全部关掉，详细可以看这个视频：[Flyme去广告 Flyme系统自带的广告太多了](https://www.bilibili.com/video/BV1eb411E7oU)。

* 音乐，视频播放器让你开会员，给你推各种各样的歌和视频。
* 日历也不老实，什么星座运程，热门，“礼包”，球队比赛新闻，引导你去别的地方
* 浏览器自不必说，默认的空白页变成了类似2345导航网站的页面，你的每个点击都在贡献CTR，最后变成钱。
* 手机管家里面也是一堆广告，安全播报，应用推荐下载
* 连壁纸里面都有有，推送节日壁纸我可以理解，为什么壁纸程序要“精选内容推送”啊？
* “资讯”就更不用说，整个就是一个大广告软件，配上一大堆UC震惊标题新闻。
* 即使你都关闭了，还有推送

我当时为什么买魅族呢？说来好笑，同样性价比的有魅族和小米，我知道小米广告多，业内素有广告米的名号。我前一部手机是魅族MX3，那个时候大家还不会塞广告，我的体验相当不错。 加上MX3从外观到UI设计的真的很漂亮，所以买了魅族。

可是看到这么多广告，我也算对魅族彻底死心了，准备刷个国际版，这辈子不再买魅族了。在我准备刷魅族的最后关口，魅族手机不幸掉海里了，我干脆买了Google的Pixel，这是后话了。

广告也是历史悠久了，很早以前的国产手机（非智能机）除了开屏广告以外，还会内置一些删不掉的，需要交钱才能玩的小游戏，或者是一些订阅收费彩铃的入口。

## 权限控制弱

小米的照明弹算是对流氓App打响了第一枪（居然是广告米！）。

国内的软件，大多权限要的特别多。 这里都不说脉脉这种不读通讯录就不给用的流氓，很多软件，就算你不给权限，也有办法旁敲侧击的获取。 比如你不给应用列表，那就挨个尝试启动，比如尝试启动微博，启动成功了你肯定装了。再比如有文件夹读取权限的，就去读闪存里面有没有Weibo的文件夹，来判断你装了没有。 GPS的权限没有，但是如果能读取照片文件，就用照片里的GPS信息。 诸如此类，不胜枚举，你的一言一行，都被扒的干干净净清清楚楚，这些数据送到公司去，机器学习给你安排的明明白白。

定期收集的数据，通过流氓一般霸占在后台的进程发送到公司的服务器，然后用这些数据生成用户画像，以后给你推广告。

自从2010年，GFW屏蔽Google以来，Google的统一推送服务在中国是不能使用的，于是每一个应用程序都要启动一个后台服务来接受消息，顺便上传一下采集到的用户数据。很长一段时间，Android手机的内存几乎一直比iPhone要大，电池也大，但是没有iPhone流畅，电池也不耐用。 原因之一就是后台程序太多，占用内存太多。为了续航和流畅度，手机厂商想尽办法杀进程，App就想尽办法防杀，什么注册服务、自启动链，小小的手机里，天天都是攻防战。 当然，一部分和手机合作的软件厂商就可以获取更多的权限，来保证手机的正常运行。

这也不是国产系统的问题，这算是Android的通病，但是这个问题在国内更加严重. 因为Google离开中国以后，Android缺乏统一的推送机制,加上国内厂商大多是Fork一些非最新版安卓的源代码加一层壳改造开发，一些最新的权限控制更新也不能及时应用。

## 你的手机不是你的手机

最近，V2EX上有爆出了VIVO的手机存在后门，会将自己当作mCDN使用：[Vivo 手机某系统进程开放 55555 端口疑似用作 mCDN](https://www.v2ex.com/t/778678)。 同时还爆出系统内启动快应用不用伪协议，而是启动一个HTTP服务器，监听端口，而且还将这个端口通过uPnP暴露到公网上，几万台手机可能就会变成肉鸡。

可以看出，手机厂商已经不满足于给你推送消息，你购买的手机，将会变成手机厂商的分布式数据采集和计算设备，而你除了要付手机的费用，手机卡的费用，网络的费用，还得充电供着，不允许自己刷机或者Root，刷完软件就连硬件都不保修，就算你不要保修，他们也会通过设置一些标识位，防止国内的用户用上无法让他们赚钱的国际版系统。

这也不是近几年才有的新鲜玩意儿，早10年，安卓爆发的前夜，有很多山寨机就会偷偷发短信去“订阅服务”来赚钱。

## 国外的月亮是不是更圆

要圆一些，但也不是那么圆。 苹果也好，Google也好，也都在收集你的数据，苹果曾经为了刺激用户购买新的设备，以电池老化为理由，给旧设备降频，你的手机感觉更慢了。 但是至少：

* 推送是统一的，不至于费电
* 权限管理，尤其是通知管理是严格的，App不至于出现群魔乱舞的状态
* 系统App里面是没有那么多推送和广告的，系统是纯净的

归根结底，所有的资本都想做恶，但是在欧美日本这些国家做恶的成本太高了，让这些厂商不得不收手。

## 开源的手机是不是智商税

是，也不是。买开源手机之前，你一定要知道你买了一个什么。 大多数的开源手机都是一个ARM64的主板，基于Linux操作系统定制的开源系统。 而Linux操作系统是几乎没法运行iOS或者Android的手机软件的（当然Android模拟器是一个选择，但是实在不算一个好选择，或许可以试试看这个：[https://anbox.io/）。](https://anbox.io/%EF%BC%89%E3%80%82) 而且同等配置下开源手机会更贵，道理也很简单，产量不够，系统开发，手机设计，开模的成本分摊到每一个手机上就更高。

但是另一个角度来说，你失去了手机的软件，但是你获得了几乎所有电脑端的软件，你的手机可以运行Python，GCC，乃至几乎任何能在你的桌面端Linux跑起来的软件（只要内存够）。

作为在中国使用的手机，可能绕不过去微信，QQ等软件，选开源手机基本意味着智商税，但是如果你需要一个备用机，或者你是一个开发人员（或者运维），用手机可以在出差或者旅游的时候解决一些问题的话，可以买一个，就当买一个超小型电脑，而且Telegram，Twitter，Mastodon之类的服务都可以照常使用，娱乐问题也不大。

## 如何选择手机

* 不愿意折腾的，或者没有技术折腾的，选iPhone。
* 能翻墙的，选原生Android机（Google Pixel这样的），或者能刷成原生Android/其它你能控制的纯净一些的Android系统的手机。
* Linux开源手机不适合在中国日常使用，但是可以作为备用机。

顺便一提，我很期待Windows的手机（虽然期待Windows的手机，但是我对Windows的印象仍然很差，尤其是强制更新这一项），Windows应用商店生态能够勉强支撑日常使用，如果ARM能够高效转译执行x86的指令集的话，生态上就是无敌的存在了。


# 访谈：为什么毛泽东时代工人的积极性很高？

[原文链接](https://zhuanlan.zhihu.com/p/153060273)

按：工人阶级在毛泽东时代有极其高昂的生产热情，他们对工厂的热爱、对生产的热情甚至到了我们现在无法相信的地步。比如他们会主动申请义务劳动，不要任何报酬；如果领导安排义务劳动没有安排到他，他会觉得这是领导看不起他，心里面会非常不舒服。又比如有些工人为了抢任务，会连续工作24小时，领导拉都拉不走。在主流的观点看来，这种热情是一种畸形的、违反人性的热情，这种热情的出现就是一种错误。那么事实的情况又是怎么样的呢？如此高昂的生产热情究竟是怎么产生的呢？

我为了搞清楚这个问题，走访了数位毛泽东时代的老工人，通过访谈我形成了一个初步的结论：高昂的生产热情之所以能够产生，是因为工人真真正正的感受到了自己是工厂的主人；这种主人的地位并不仅仅是体现在口头上或法律文件上，而是渗透到了生产、生活的方方面面。下面是我们之间的访谈记录。 问：我接触过一些老工人，那些工人在提到毛泽东时代的时候都很感慨。常常说当时的工人是爱厂如家，所以生产热情非常高，而现在的工人对工厂没得任何感情得。我想问，当时的工人为什么会对工厂产生这种感情呢？ 罗师傅：小李，你这个问题问得很好。这个世界上没得无缘无故的爱，也没得无缘无故的恨。我们为什么会爱厂如家，原因很多，但我认为首先是因为这个厂关心工人。当时的厂对工人的关心可以说是到了无微不至的程度。

举个例子来说，我是58年之后参加工作的。工作没得好久，有一次家里面来信，说我爸爸身体不好，现在家里面很困难。我当时也没有给厂里面说，但是在私下给朋友说话的时候说漏嘴了。过了一段时间，家里面来信说收到了50块钱，我都不晓得是怎么一回事。后来我问领导，才晓得是工会寄的。 罗师傅正准备往下说，黎师傅打断了他的话。黎师傅是退休干部，以前在团委和工会都干过。黎师傅说：小李，当时工会领导那真的是把工人的利益装在心里面的。当时每个班组都有一个工会小组长，他负责了解班组工人的情况。方方面面的情况都要了解：家里面困不困难？爱人是干什么的？小孩是否该上学了？最近有什么状况等等。班组把情况汇总到车间一级的工会，车间再把情况汇总到厂工会。所以说工会对整个厂里面所有工人的情况、困难那是一目了然，一旦出了问题马上就给你解决了。他甚至都不用你来说，自己主动就给你解决了。举个例来说，凡职工生病请病假3天以上的，工会领导就必须要去关心。无微不至啊，就到了这种程度。 张师傅是厂里的处级干部，改革后在职工大学当校长。张师傅说：我给你举个例子，切身的体会。我55年的时候在一线当工人，得了胃病，经常去医院，很严重。当时袁景良是我们工会的劳保委员，他晓得了这个事情，就提名让我去疗养。单位就送我去疗养所疗养了50天，疗养所的伙食非常好，标准是21块/月，我自己只出9块钱。疗养了之后我的胃病基本上就好了，到现在都没有犯过。 你问我为什么工厂会这么关心工人？道理很简单，因为我们是工厂的主人。 问：我还有一个问题想问一下。比如现在，很多干部最初也是很优秀的，但他们后来就慢慢变质了，开始搞贪污腐败。那当时出现这个问题怎么办呢？也就是说怎么防止优秀的人变质呢？

周师傅自己有当领导被群众检举的经历。周师傅说：当时的贪污腐败很少，各方面的原因都有。比如当时对现金的管理很严格，30块钱以上不能使用现金，只能使用支票，这就让你贪起来很困难。再比如当时人们思想觉悟很高，觉得贪污是很可耻的一件事情，使得你自己就不愿意去贪。现在完全搞反了，贪得越多觉得你越能干。 另外还有一个很重要的原因，就是群众的监督很厉害。当时所有账目都要张榜公布，任何人都可以查。我在电力系统的领导岗位，在文革的时候我管经济。当时我经手一笔款子，8万多，有3角7分钱对不上帐。就因为这么一个事情，我被人检举，上面派了7个人来查我的经济，前前后后查了半年多时间。最后终于搞清楚了原因，不是我贪污，而是账记错了，所以给我定了一个“贪污行为不按贪污论处”。当时的群众监督就能严到这种程度，你说还有哪个敢贪。 梁师傅：毛主席说过：真正的铜墙铁壁是人民群众。当时干部和群众是住在一块的，干部家里面是什么情况群众都晓得。所以没得人敢贪，群众把你盯着的，一有任何问题马上就检举你。工人的检举途径也很多，可以写大字报、小字报，可以直接找党委书记反映。还有就是当时每个月有一次民主生活会，你可以在上面给领导提意见。而且他还拿你没得法，因为领导是没得权力开除工人的。 比如说我们厂的副厂长利用权力给自己分了一套房子，群众在民主生活会的时候反映到党委，结果这个人被党内严重警告处分，房子退回，级别从副厅级降到副处级。 张师傅是厂里的处级干部，改革后在职工大学当校长。张师傅说：我说一点个人的看法，和他们的认识有一些出入。我认为要分两个方面来看，第一、当时对领导的监督还解决得不够好；第二、由于工人敢用大鸣大放来监督领导，所以这个监督问题比现在解决得好得多。我说不够好是因为当时有些领导滥用权利的问题没有得到解决，比如我们厂的领导杨××，他喜欢在背后用小动作整人，这就明显是乱用权力。文革的时候群众写大字报揭露他的问题，后来他就被群众批判了，批判了之后整个作风大大改变。呵呵，不过改革开放之后老毛病又犯了。

范师傅70多岁，是全国劳模。范师傅说：小李，我还想给你说一点，要理解什么是主人，就要理解什么是奴隶。主人的对立面就是奴隶，奴隶的主人是不信任奴隶的，怕他们偷懒，所以随时都拿着皮鞭抽打他们，强迫他们工作，根本不把他们当人，只把他们当会说话的工具。 说得难听点，现在的工人就是这样。领导和工人处于一个完全对立的状况，工人在一线干活累得半死，领导在办公室吹空调。工人稍微不随自己的心意就破口大骂，动不动就克扣工资。在工厂里面工人没得半点说话的权利，你能做的就只有服从，除了服从还是服从。你说，这不是奴隶是什么？ 以前可不是这样，当时提倡鞍钢宪法，充分的相信群众的自主性，让工人以主人翁的态度去干活，而不是厂长拿着棍棒驱使工人去干活。当时是干部带头参加劳动，以此带动群众的生产积极性。比如我们厂，副处级以下干部基本上所有时间都在生产一线，干的活比工人还多。正处级以上的干部每个礼拜五都要参加劳动，还要向工人拜师学艺。当时干部参加劳动那是真的在劳动哟，我就听说过有些车间的主任、书记亲自上一线结果中暑晕倒的。 当时还提倡让群众提合理化建议。我们厂还专门设立了机械化车间去实施工人提出来的合理化建议。当时工人一共提出了4万多条建议，最后根据这些建议做出了400多项技术革新。现在呢？领导根本看不起工人，他觉得技术革新是技术员的事情，你工人不配提意见。 张师傅：当时的工人在生产过程中也处于一个主人翁的地位。就拿生产任务的制定来说，现在生产任务的制定都是厂长说了算，当时可不是这样。一个生产任务下来了，厂里面下到车间，车间下到班组，班组传达到每个工人。然后让工人讨论，看应该怎样完成这个生产任务，如果觉得这个生产任务规定得不合适，工人就提意见让上面修改生产任务。比如有一次一个生产任务下去了，让一个工人轧了碳素钢马上轧锋钢，工人觉得这种生产任务安排不合理，于是提出意见，后来就把生产任务修改了。 如果提了意见上面不听怎么办？那工人就可以继续提意见、可以去闹，你当官的把他没得办法，因为你没得开除工人的权力。当时有句俗话：你当10年官，我11年都不犯法，你把我没得办法。我给你举个例子。当时碳素钢钢锭出来之后，为了保证钢的质量，按照操作规程，要切掉1.5%的量。后来我们在实践过程中发现，只切掉1%的量也能够保证质量。这样每吨钢锭就可以多出0.5%的钢材。我当时在团总支任生产委员，就和另外一个工人一起向上面反映这个问题。结果车间和厂里面都不支持我们，说这个东西是苏联专家定的，不能改。我们就和上面顶着干，连续几百次违反苏联专家定的操作规程，只切1%的量。结果厂里面拿我们也没得办法，因为我们没出任何质量问题。后来我们把问题反映到冶金部，冶金部的让我们把实验数据交了上去，最后冶金部批下来同意了这种操作方法。 周师傅：工人为社么觉得自己是主人，还有一个很重要的原因，工人和干部的待遇是差不多的。以吃粮为例，当时根据工种定粮。困难时期我在市委工作，当时市委干部是21斤粮，工人普遍比干部多，某些工种能到50斤粮。 梁师傅：有一次我们单位发奖金，干部奖金是4块钱，炼钢工人的奖金是6块钱。由此可见，当时的干部才真正是人民的公仆、人民的勤务员。那个时候当干部只有多做事的，干得不比工人少，拿得不比工人多。当然，你问我们工资有没有差异，也有差异，但是这个差异很小。 张师傅：你说工人和干部的工资有没有差距呢？这也有差距，但这个差距小，很多工人的工资比干部还高。我们厂里面有一个8级钳工叫李孟虎，工资待遇相当于县级干部的收入。他一个人的工资就够全家人生活，而且还生活得很好。他有8个小孩，后来这些小孩有上中专的，也有上大学的，很有出息。你想想现在，你一个工人的工资能够养活8个小孩？还要读书哟！根本不可能。 问：毛时代每个人能够得到的物资那么匮乏，为什么小偷小摸的利己现象反而很少呢？为什么到了80年代改革开放了，物质财富丰富了，消极怠工的现象反而越来越厉害了？工人为什么会偷懒呢？西方经济学有一个观点叫做“公有地的悲剧”，或者“搭便车”，来形容出工不出力的社会情况。在毛时代，工人们中间有“搭便车”的情况吗？如果有，那么是用什么样的方法对待的呢？ 老工人李师傅：拿我们钢厂为例。承包以后，那个厂长想给大家发奖金想刺激工人干活，可工人一看，你那七大姑子八大姨都成了领导了，这些人一起挖社会主义墙角，工人干脆不干了。工人丧失了主人公地位，他不认为是在给自己的厂子干了，这是你厂长的厂子，你厂长想拿多少就拿多少，所以工人都不愿意干了、消极怠工。资产阶级就借着这个污蔑工人阶级“大锅饭、养懒汉”。 偷懒是厂长承包制以后。你厂长一承包，七大姑八大姨都发财了，每年拿几十万的奖金，工人一个月才一二百块钱，工人生气咧，所以磨洋工。这与计划经济没一点关系。他把自己犯的错误强加到计划经济、工人头上去了。 一位老工人：毛泽东时代工人们受的教育就是要大公无私，所以大家的思想都很好，私心不重，再说，厂子是工人自己的，不会去偷的。当时也有“搭便车”的，不过是极个别人，通常大家都会对他进行批评教育，一般都能改正。那时对小偷小摸的现象定性是很严重的，谁要是偷拿工厂东西，大家都会很看不起他，舆论压力很大，并且厂里也要严肃处理。 改革开放后，所有制的变化把人的私心都给挖出来了。主流媒体都是在告诉人们如何赚钱发家，整个社会的价值观都发生了变化，金钱至上。从上层领导就开始多拿多占，所以工人小偷小摸的现象多了。 毛泽东时代普通工人也可以参加技改和管理工作，有专门的实验室供工人从事技改活动，虽然学历低，但在实践中也是能有所创新的。工人们都是主动参与技改和管理工作，主动参加义务劳动，不让他参加他才不乐意呢。那时候要的是荣誉。现在你给人家钱，人家都不干。” 常师傅是××针织厂的老党委书记。常师傅说：共产党的最高宗旨是消灭私有制。过去，工人真正是工厂的主人，现在工人沦为雇佣工人。纺织局局长李\*\*，原来是纱厂厂长，纱厂产品为生产原料，不可能卖不出去，他把纱厂搞破产后，转为私人，2600名工人成为雇佣工，供其榨取劳动剩余价值。80年代有一些烟厂的工人，会把烟塞到饭盒里带回家，但如今的奴隶地位决定了这种小偷小摸的现象会有增无减。


# 杂谈

## 女拳参考文献

* ShhShhShh. 我只说一点，我无原则无底线永远站在自身性别和自身利益这边。\[J].微博,2023.6
* 萤崽。你不必嫌弃我的残疾,因为我不会成为你的妻子或朋友\[M].抖音:抖音教育出版社,2025.
* 只要没结婚，订婚、男友、未婚夫等身份都没有法律权利，自然也不用承担丈夫责任，这一点在《 山西大同订婚诬陷案 》中有记载。
* 同意是可以撤销的。----中国妇女报有记载
* 同意可撤销----此事在南昌锜振动案中亦有记载
* 不要利用舆论干涉司法。----山西大同案也有记载
* “舆论不能代替法律”——《中国妇女报》也有记载
* 喜欢不等于要结婚----女方聊天记录

```
@子清: 参考文献：
[1]莹崽. 你不必嫌弃我， 因为我不会成为你的妻子或朋友 [Video].2025.06 抖音短视频
[2] 唐妞. 男人的数学也就这水平，连不等式都看不懂 [Video]. 2024.09 抖音短视频。
[3] 小慧君. 如果不愿意给女性让座，那你不配拥有社会地位 [Video]. 2025.03 抖音短视频。
[4] 芝芝老师. 女孩可以不做家务，但男孩必须要会扛事 [Video]. 2025.01 抖音短视频。
[5] 阿梨姐姐. 我说了不想结婚生子，就不需要你们的意见 [Video]. 2024.12 抖音短视频。
[ 6 ]温柔刀. 别劝我和解，我是来讨债的 [Video]. 2024.08 抖音短视频。
本言论已参考相关研究成果，详见上述文献，转载请注明出处。
本评论基于前人女权语料库，具有一定实证基础，望周知
致谢：中国妇女联合会、《中国妇女报》、山西省大同市中级人民法院、华东理工大学
```

## \[2022-08-09] 吐槽一下国内的数据竞赛平台

本来不会参加任何竞赛的了，但是和同事在讨论项目方案的时候，同事提到某个竞赛的Top1方案不错，于是我准备去登录一下这个系统然后下载数据复现一下。

于是我就遇到了[datafountain](https://www.datafountain.cn/)。

首先得登录，问题不大，我看到输入手机号和微信登录，果断选择微信，结果微信连接完又让我输入手机号。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-acea97ad58ea28827afbfe607d53fe319162937f%2F6pd1vb.jpg?alt=media)

这都是小事，不就是给白嫖了微信号吗？给他！

于是我输入手机号，给我发了个验证码，我悲哀的知道以后收到的垃圾短信里有一份datafountain的功劳。

我准备下载数据，先是同意协议参加竞赛，没问题！

当我点击下载数据的时候，datafountain让我填写少许的个人信息：

* 生日
* 居住地
* 学校
* 专业
* 学历
* 入学时间
* 毕业时间
* 职业经历
* 职位
* 所在行业
* 技能方向
* 姓名
* 身份证号

以上都是必填信息，少写一个都不给过的。

填完信息，我开始下载数据，我满心期待，我交出了我所有的个人信息，我现在对datafountain已经是赤身裸体的状态了，毕竟我爸妈都背不下我的身份证号。再说，毕竟是**中国计算机学会**举办的比赛，数据也应该是很有价值。

结果从数据大小我就开始感觉不对劲儿了，测试集30M，训练集60M不到，一看，只是挑选了某个大数据集的一个子集。

子集就子集吧，能用就行。下载完所有的文件，我发现标签文件和下面的数据简介对不上，还缺一个文件死活找不到。那么使用方式就只能靠猜了。

本来吐槽企业随便拿点自己的生产数据，然后把自己懒得解决的问题扔出来来举办什么数据竞赛是不太体面的行为，现在看来，前者好歹有实际应用背景，大部分企业还是能做好数据脱敏、清洗和整理。

而这次遇到的竞赛，则是彻底击穿我所看到的下限。

## \[2021-06-26] （转载，来源丢了）教育能否改变人生

很多人认为自己的命运是被高考改变的，这种看法完全错误。

真正改变命运的就两件事：

1、全球化。

2、城市化。

很多人就是喜欢把自己的成功归因于个人成功。但是没有这两条，全给你考上清华北大有用吗？

美国1929年大衰退，哈佛的开电梯都多的是，开电梯都是不错了。

而且一定要明白一点，你看起来阶层上升了，但是大多数人在全社会的总排名没有变，只不过原来你们家在村里一年多吃一顿肉，现在你在上海可能多了一套房，你觉得阶层上升了，其实并没有，社会总排名不变。

但是，升老师不是说这没有意义，相反这非常有意义，你要排名没有用，蛋糕做大了，他开法拉利，你开丰田，这才是真正的价值。’

就是教育本身，也受益于这两点，让教育公平很多，大家回忆一下，你的上一代考上大学的有多少人，考上的是不是都是安阳师专，常熟师范，能考上个大专就祖坟核爆炸了。

现在你考上了985 211是你比上一代人能吗？不是，因为扩招了，为什么扩招了，因为城市化，因为全球化，需要更多有文化的人了。

这不是教育本身能做到的。

还有一点你要感谢的，是你的父母，给你了一个不错的社会总排名。

至于以前的大学生，考试上的有改变命运的没有，但是以前除非是天才，考大学是要反复复读的，俞敏洪考了三年，马云也考了三年，实际上就是升老师以前说的，如果你把高中延长到5-6年，最笨的人也能考上北大。

但是呢？什么家庭能支撑一个青壮吭哧吭哧读三年书不干活。你说应不应该感谢的你父母的排名。

“寒门出贵子”，这件事，从一开始就理解错了。

什么是寒门？

寒门又叫寒族，也就是庶族，对应的是士族，寒门是跟高门对应的，这是魏晋兴起的概念。

高门是大地主，大官僚，你祖上差不多从汉代一直是官宦，注意，没有断过，那你是士族。

你要是断了，有几代没有当官，是寒门。或者说，你后来自我奋斗，终于成了中小地主，好了，你算个寒门。

看见了没有，这里都是地主，没有农民的事，甚至没有商人，没有手工业者的事。

所以寒门是谁，曹操他家是宦官出身，后来做到太尉，他还是寒门。

不是说没有高考，是有高考，这个路子也一定不是你的，寒门出贵子，怎么办呢？

走军事路线，所谓的将门。

不要觉得将门很好听，在高门眼里将门是骂人的。

司马懿他家也是寒门，只不过从他这两代起开始好转。后来司马炎当皇帝，跟他的妃子胡贵嫔玩飞行棋，结果胡娘娘把司马炎手指抓破了。

司马炎骂她：你他妈真是个将种。意思是你家是武将之后。

结果胡贵妃也不客气：说我爹北伐公孙，南拒诸葛，我当然是将种。

什么意思呢？我爹当武将，都是跟着你爷爷司马懿，不是打公孙渊，就是干诸葛亮，我是将种，你他妈也是将种，你全家都是将种。

司马家就是寒门，只不过就是后来掌握了话语权，给自己在史书里给自己加了四个字“服膺儒教”，啥意思呢？我们虽然是将种发家，但是我们也是文化人啊，相当于领导干部进修北大博士。

所以寒门从一开始，就不是真的底层，它应该换个名字，叫”新贵“，换到今天，你得是知名企业家。新贵出贵子，你就能理解了，新贵本来就能出贵子。

而且新贵也要拼，为啥还有个军事路线给你呢，因为打仗要死人，人家士族不想死人，所以你打仗能出”贵子“。

这就是为什么豁出命去，能出贵子。一张考卷就改变命运的事从来没有，你觉得你是分酬，人家那是血酬。

这不是说魏晋没有考试是这样。后来有考试也是这样。

宋代的科举，产生了不少科举世家，只要有一个人中了，后代就容易中了。为什么？人家有家学啊，考试技巧，作文思路啊，这点你在学校里学不到，只能老子教儿子。

你看《儒林外史》就明白了，当然你光看一个中学课文节选《范进中举》你看一辈子也不明白。

范进为啥能中举？不是因为他老丈人是屠户，屠户有点小钱，是个中产，有点这个原因，但这不是全部，范进中举是因为周进。

周进做了广东学道，才让范进中了秀才，而且跟他说了一句话，说你只要考举人，一定能中的。

范进这个人的文章是好的，但是，他过不了秀才这一关，因为他适合过举人这一关。明白什么意思吗？

升老师给你说明白一点，就是过秀才这一关，需要秀才这一级的好文章，过举人这一关，需要举人级的好文章，过进士这一关，需要进士一级的好文章。而这三种文章，是不一样的好文章。

范进的老师周进，就是卡在了举人这一关，所以考到54岁，还是一个秀才，童生，因为他擅长的是进士级的好文章，范进这个人，他卡在了秀才这一级，但他其实是举人级的。

这就是教育的吊诡。

这才是《儒林外史》要告诉给读书人的，这个道理，升老师不给你讲，你能明白吗？你读一辈子四书五经，滚瓜烂熟，出不来的，因为掌管你命运的，选秀才你都过不了。

但是人家家学有啊，考秀才怎么过，考举人怎么过，甚至哪个主考官，喜欢什么文章，门儿清。

《儒林外史》里有个出书的，叫马二先生，他出什么书，出《五年高考，三年模拟》，那个时代的高考公考辅导书，但是马二先生这个人，考了很多年举人，没考上。这就相当于，一个高考失败的人，来给你讲高考，他能提供什么经验，他只能提供考童生的经验，还有考举人失败的意见，但是你要考的是举人啊。

这是不是就是今天的高考课外辅导，公考辅导，都是没考好的人给你讲，你掏几万有用吗？没用。

所以后来一个高翰林说出了科举的诀窍，你看四书五经没有用，你要琢磨主考官的文章，琢磨他的喜好。

翰林都是进士出身，这才是真正的考试技巧。

所以你明白，为什么《红楼梦》里，贾政给儿子娶媳妇，第一个儿子娶的是国子监祭酒的闺女李纨，第二个儿子贾宝玉，要娶的是，探花郎的闺女林黛玉。

你明白为啥薛宝钗、史湘云这些人，总劝贾宝玉要科举，林黛玉从来不劝吗？因为人家家是真考过的，其他人劝也是瞎劝，知道你天天K书也没有用，高考不拼那个。

古代的翰林，都是穷官，但是翰林的闺女从来都不愁嫁。

改变命运，是靠考试吗？是靠教育吗？

所以，教育不是改变不了命运，是从来没有改变过命运。

## \[2021-04-11] 肇事逃逸

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-789c538e884ab373c37acc138cc8ff3116eb2101%2F2021-04-23-21-58-21.png?alt=media)

来源：[你见过最恐怖的事是什么？](https://www.zhihu.com/question/352776383/answer/1753797237)

没想到有这么多人看哈哈哈哈，虽然但是我是个姑娘！！！！有好心人帮我爸说话说的是“自己的儿子”！！

—————分割线以下原回答。

车祸，我是被撞的那个。

那天我新买了一台笔记本电脑，苹果的，m1芯片那个，超级开心，用那台电脑在自习室认认真真的看了一下午网课，从3点看到晚上10点，就中间恰了个饭算是休息。

自习室离家要经过三个十字路口。

我是骑哈啰助力车回去的，macbookair很轻，我图背去自习室省力。

在倒数第二个十字路口那边，我直走，他右转。

我也不知道这个哈皮是拿刹车当油门了还是咋，速度之快以至于我躲都躲不开，啪的一下我就被撞倒了，很快啊，我的助力车也飞出去了。

就在地上翻滚了一圈，整个人都是懵的，我特别害怕我的笔记本坏了，我才刚买了一天。

这辆车是超速了，我直走的时候眼观六路耳听八方都没看到我身后有辆大众。

好家伙我当时甚至没有什么痛感，撞我的一瞬间根本不是疼来形容，可以说是恶心。

好在我福大命大，没啥大事，那个时候十字路口有个好心人开车把我送去医院了，在我爸来之前陪我做检查啥的都是这个好心人，骨头没事，没有什么皮外伤，就是有一处伤口里面的肉坏掉了，需要割掉缝针，不然会感染。

急诊室里还有另一波，那天晚上我病床旁边的哥哥就有点惨了，是被刀子捅了，在外面等我的好心人把耳朵竖得老高偷听是为啥被捅，好像是捅他的喝高了又被言语激了。

其他的只需要身体自己恢复就好了。

当天晚上睡觉的时候痛感就来了，不能翻身，早上四五点被疼醒，但是很奇怪，车撞的是我上半身，我的疼痛全集中在下半身，那段时间我的腿没有办法直立，脚踝那边一用力就疼的嗷呜嗷呜的。

就这样为了带去自习室学习而买的macbook，成了我在床上看网课的工具（早知道买个败家之眼）

那天晚上我真的以为我要没了，被撞之后暖流一直在我心肺那边，我以为我肺泡炸了，动脉破了，我打电话很平静的告诉我爸我肺泡炸了，我动脉应该裂开了，我要是没了你们就再生一个。

我爸气的说你肺泡炸了你早没了。

那天晚上我爸也很好玩，最颤抖的语气说着最硬的话，来医院的时候眼睛都红了说话都颤抖了，把好心人垫的医药费还给人家之后我爸觉得高低得送点啥，我爸车上啥也没有他兜里的烟也就剩半盒了，他一着急把车里的玻璃水跟人家了，给那个好心人逗的。

以上是happy ending 了吗？

不是，我和我爸都觉得人没事就是万幸，准备第二天报警，撞我的人把我撞了就跑了，我和我爸也不想追究逃逸的责任，就寻思赔个医药费就完了，就算是赔钱这东西交强险

三者险都能报，赔也是保险公司赔。

想不到叭第二天下午我接到了交警队的电话，他们说我肇事逃逸

，因为人家撞我我车坏了。

e克丝q丝蜜？

我问“我不懂啊这怎么算逃逸。”

他说“车主前保险杠坏了是因为撞你撞坏的，你当时没报警直接离开案发现场就算逃逸。人家要是不谅解你你还要被拘留，我不是跟你开玩笑”

我问“这怎么还上升到拘留了。”

他说：“你不懂是吧，没事，我会让你懂的。”

我一直很愿意相信公权力的。

直到那天，我脊背发凉，发现气冷抖根本不是一个梗，是一个很真实的身体反应。

后来也懒的阐述怎么解决的了，好在那天电话我录了音了，我爸交警队有很好的朋友，交警一开始说那边没监控，我爸的朋友给我们调了监控，又换了一组人给定责，车主全责。

倘若这个我爸和这个朋友不认识呢？

我又该怎么办。

编辑于 2021-06-09 23:46

## \[2020-12-04] 冲塔趣闻

有人问了个问题，“各种语言有些什么书写奇葩的单词？”，有人回答：“众所周知，“卤”是洗手液的意思。”

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-01c386881a83825cef3c28d532a7f0263e71fac4%2F2020-12-04-11-50-51.png?alt=media)

又有人回复，难道不是打废了的坦克吗？ 这个时候有人发了个：“点点点点点人？”暗喻六四事件的那张著名照片：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-cdf15704aba05c18d52a121159a936fe5ade4323%2F2020-12-04-11-50-38.png?alt=media)

随后评论区就被关闭了。 哈哈哈哈哈哈哈。

## \[2020-10-19] 假如高考改为全国统考

不用假设，19年前就是一张卷子，连分数都没有按区域矫正过，真正的公平竞争。

来源：<https://www.zhihu.com/question/52992122/answer/207896783>

说一个和自己工作相关的有趣案件。

2001年的时候，高考还是全国一张卷，分省录取。当年北京文科重点本科录取分数线为454分，一般本科429分，专科360分；山东文科分数线则夸张得有些离谱，重点本科录取分数线为580分，一般本科539分，专科为509分。分差竟然到达近130分，山东的专科分数线都比北京重点本科高55分。理科同样如此，山东理科重点线高达607，而北京只有488。注意，这是同一张试卷。这事儿吧，也怪当时山东省考试院教育厅办事不密，没用标准分

，直接用了基础分，直接显示出考试原始真实成绩（注意用标准分是不知道的）。

山东青岛三名当年参加高考的考生——这三名考生分别是青岛铁一中 高三四班姜妍(理科，高考成绩522分)、青岛一中高三四班栾倩

(文科，高考成绩457分)和青岛15中高三九班的张天珠(文科，高考成绩506分)，由于未能达到山东省当年的重点大学的分数线，甚至连专科线都没达到，别说上大学了，只能复读或者去职业技术学校。

不知咋的，这三个考生竟然做出了个什么有趣的时期，他们决定运用所学知识为自己维权，他们不是专业法律人士，但宪法里面那条：公民享有平等的受教育权还是知道的。于是，他们搞出来个大新闻：山东三考生向最高人民法院状告教育部违宪。

2001年8月23日，他们分别委托律师，以邮寄方式向最高人民法院起诉教育部侵犯了公民平等的受教育权。理由是，由于教育部以制定招生计划的形式，造成了全国不同地域考生之间受教育权的不平等，尤其是北京地区的高考录取线大大低于全国平均水平，比山东低100多分。考生的诉讼要求是请求最高人民法院确认教育部所作出的“关于2001年全国普通高校高等教育招生计划”的行政行为违法，并向教育部发司法建议书

，以避免今后再发生类似的行为。当时还开了一个小型的新闻发布会。

其实吧，这三考生一开始就没准备和教育部硬刚，他们也不敢硬刚，也刚不出结果，也不可能有结果，此例一开的话，后果不堪设想。他们一开始的目的就是为了引发媒体关注，搞出个大新闻。所以他们才会直接把案子告到最高人民法院。按照司法程序，他们应该先走地方中级法院。

果然，９月３日，最高法院告知他们：根据《行政诉讼法

》第十四条第二款规定，对国务院各部门或者省、自治区、直辖市人民政府所作的具体行政行为提起诉讼的案件，由所在地中级人民法院受理。也就是说，３位当事人应该到北京市一中院起诉。鉴于以上原因，诉状寄回。

到了北京 之后，三个学生的思想相对有点动摇，还是不敢直接刚教育部，她们有许多的担心，毕竟 还担心将来的生活会受到影响。将来的学习会受到影响，包括毕竟她们知道这次不能通过这次事件，然后另行找到一个学校来上，所以她们有可能要复读，有可能要到职业学校进行学 习，又担心教育部门将来会对她们的 学习生活造成影响。

后面就是阴谋论时间了，有人说教育部去做工作了，有人说教育部施压了。9月8日，三考生撤诉，不在上告青岛二中院。

这个案件宪法学里面还蛮有名的，也蛮有趣的。

后续，2002年高考取消全国统一命题，开始分省自主命题。

* [状告教育部侵犯平等受教育权 青岛考生进京递诉状](https://edu.sina.com.cn/l/2001-08-23/14601.html)
  * [Archive](https://archive.ph/mfwsC)
* [教育部酝酿改革 青岛三学生停诉侵权](https://edu.sina.com.cn/l/2001-09-09/15758.html)
  * [Archive](https://archive.ph/6iSdR)

### [高考录取分数线悬殊太大!青岛三考生状告教育部](http://news.sohu.com/17/76/news146347617.shtml)

中新网济南8月23日消息：要状告教育部的三名青岛考生与其代理律师昨日一起乘飞机赶到北京，23日上午，考生们将向最高人民法院递交诉讼状。

据齐鲁晚报报道，这三名考生分别是青岛铁一中高三四班姜妍(理科，高考成绩522分)、青岛一中高三四班栾倩(文科，高考成绩457分)和青岛15中高三九班的张天珠(文科，高考成绩506分)，她们三人将分别向教育部提起行政诉讼，诉教育部所作出的关于2001年全国普通高校高等教育招生计划的行政行为侵犯了她们的平等受教育权。

张天珠说，她是在8月初高考录取分数线划定以后有了这个想法的。在青岛，文科专科提档线是509分，一般本科是539分，重点本科是575分，而北京的重点本科提档线是456分。两地分数线差别悬殊，她觉得不公平。

其代理律师李强和杨培银认为，教育部根据不同地域范围对招生人数做不同限定，这种限定使得不同地域考生被划分成高低不同的等级，并在不同等级中参加高考。等级之间分数标准线差异巨大，从而直接侵犯了包括三名原告在内的广大考生的平等受教育权。

两位律师说，考生的诉讼要求是请求最高人民法院能向教育部发司法建议书，以避免今后再发生类似的行为。(李艳)

### [新闻追踪：青岛三考生告教育部违宪诉状(全文)](http://news.sohu.com/42/01/news146510142.shtml)

《高考录取分数线悬殊太大!青岛三考生状告教育部》在搜狐登载后，引起网友的普遍关注和热烈讨论。尽管至尽还未有结果，但我们将会继续对这件事和中国教育改革予以关注。现将“青岛三考生告教育部违宪诉状(全文)”登载，以飨读者。

行政诉讼状

原告：栾倩、姜妍、张天珠（注：实际三人是三份诉状，为简化写在一处）

个人情况略

诉讼代理人：李强，山东德衡律师事务所律师

诉讼代理人：杨培银，山东德衡律师事务所北京分所律师

被告：中华人民共和国教育部

住所地：北京市西城区大木仓胡同37号

法定代表人：陈至立，部长

诉讼请求：

１、请求法院判决被告所作出的关于2001年全国普通高校高等教育招生计划的行政行为违反\[中华人民共和国宪法]和\[中华人民共和国教育法]的有关规定；

２、请求法院向被告提出司法建议书，促使被告以后避免作出类似违法行政行为。

事实和理由：

一、被告是具有国家行政职权的机关，并且作出了违法行政行为

被告作为我国最高行政机关的下属机关，具有对我国的教育包括高等教育考生录取进行管理、监督的行政职权。依据这种行政职权，被告在2001年4月16日作出了关于2001年全国普通高等院校教育招生计划的行政行为，该行政行为是以教发（2001）23号一个“通知”的形式作出的。

依据《行政诉讼法》以及最高人民法院关于\[执行《中华人民共和国行政诉讼法》若干问题的解释]，被告的行政行为与现行宪法直接相冲突，侵犯了宪法赋予公民平等接受教育包括高等教育的基本权利。

中国是社会主义的民主国家，这一点在制定宪法时体现得尤为明显。作为中华人民共和国的公民，原告从来就很自豪。因为我们的宪法赋予了公民如此广泛的基本权利，这其中就包含了平等权和受教育权。

平等作为我国宪法的基本原则，已经体现在各个领域。男女平等、民族平等、家庭内部成员之间的平等都是宪法的平等原则在不同领域的具体体现，而平等原则体现在受教育权上就是指公民享有平等的受教育权。

平等的受教育权意味着接受教育（包括高等教育）的资格平等，权利能力的平等。只要是中华人民共和国的公民，具备相应的行为能力，不分财产状况、居住年限、地域、宗教信仰等，在法律上都应该被认定为具有平等的接受高等教育的资格。而这种资格的具体体现，就是一年一度的国家组织的统一考试——高考。

从平等的受教育权原则出发，除非特殊专业需要特殊的身体条件，否则，所有的考生在高考当中只有一样区别，就是分数。只有分数的区别可以决定考生最终是否能够接受高等教育，以及进入哪一所高校接受高等教育。

平等的受教育权作为一种宪法权利同时也规定在《中华人民共和国教育法》中，任何人、任何组织不能以任何形式来限制和剥夺公民的这种基本权利。而被告作为一个长期主管高等教育的行政主体，明知宪法规定的公民享有平等受教育权，却以一纸“通知”将原告这种权利限制和剥夺。

被告的行政行为之所以侵犯了原告的平等受教育权，就在于被告在该行政行为中根据不同地域范围对招生人数做了不同的限定，这种限定使得不同地域的考生被划分成了高低不同的等级，并在这不同的等级中参加高考。等级之间分数标准差异巨大，从而直接侵犯了包括原告在内的广大考生的平等受教育权。

众所周知，在中国，高考是人生的一个重要转折点，能否上大学、上哪一所大学对原告这样一个尚未完全踏入社会的青年来讲可以说是决定了其今后的命运。而被告在2001年4月1 6日的行政行为却使原告以及其他许多与原告有相同命运的考生在未参加高考以前，已经被被告人为地做了区分，这种区分使得原告在高考中对同一份试卷的回答必须比其他的考生（如北京）高出100分-120分才能取得与他们相同的接受高等教育的资格。这里显然没有“平等接受教育权”，造成如此结果的，即是被告的行政行为。

二、被告的行政行为属于可诉范围

《行政诉讼法》规定了法院的受理行政诉讼案的范围。根据最高人民法院关于执行《中华人民共和国行政诉讼法》若干问题的解释第一条的规定：“公民、法人或者其他组织对具有国家行政职权的机关和组织及其工作人员的行政行为不服，依法提起诉讼的，属于人民法院行政诉讼的受案范围。”

第三条行政诉讼法第十二条第（二）项排除性规定的"具有普遍约束力的决定、命令"，是指行政机关针对不特定对象发布的能反复适用的行政规范性文件。而在本案中，被告的行政行为则是每一年分别作出，适用于特定地域对象，并且不能反复适用。因此，被告的行政行为属于可诉范围。

原告参加了2001年度的高考，分数是分，这一分数已经大大超过了北京市的重点大学分数线，然而，由于被告的行为，作为一名青岛考生，原告却连专科学校都无法入读。原告平等接受高等教育权利的受损与被告的行政行为有法律上的直接利害关系，是适格的诉讼主体，有权请求法院司法判断被告行为的违法性。根据\[行政诉讼法]第十六条的规定，最高人民法院有权管辖此案。

综上所述，被告做出的行政行为，直接侵犯了原告的平等受教育权，原告因此诉至法院，请求裁判。

此致

中华人民共和国最高人民法院

具状人：（略）

二〇〇一年八月二十日

### [诉讼目的已达到 青岛三女生终止对教育部的行政诉讼](http://news.sohu.com/16/87/news146528716.shtml)

东方网9月10日消息：记者从山东德衡律师事务所律师李强、杨培银处获悉，青岛3名状告教育部侵犯其平等受教育权的女生，将终止对教育部的行政诉讼。

律师介绍，自从8月23日向最高院邮寄诉状以后，律师和3位当事人一直在等待最高院的答复。9月3日，最高院立案厅一位姓侯的法官分别给3位当事人打来电话（因为起诉是分别起诉，不是集团诉讼），告知他们：根据《行政诉讼法》第十四条第二款规定，对国务院各部门或者省、自治区、直辖市人民政府所做的具体行政行为提起诉讼的案件，应由所在地中级人民法院受理。也就是说，3位当事人应该到北京市一中院起诉，鉴于以上原因，诉状寄回。

李强律师告诉记者，3位当事人及其家长决定9月8日终止这起诉讼。因为能顺利发起诉讼本身就是胜利，而社会对这一事件的超强度关注，已使得呼唤教育平等的声音传扬出去；再说，根据种种消息，教育部已有所触动，进一步改革正在酝酿，起诉的目的已经达到。

据了解，3位女生中，张天珠将继续复读，栾倩和姜妍分别被电大录取，但她们可能不会去读，而选择民办大学。

新闻回顾

8月22日，山东青岛应届高中毕业生姜妍、栾倩、张天珠在律师的陪同下，来到北京。次日上午，她们的代理律师李强、杨培银用特快专递将诉讼状寄给最高人民法院，起诉教育部侵犯了公民的平等受教育权。

律师意见

李强、杨培银律师认为，教育部所作的《关于2001年全国普通高校高等教育招生计划》的行政行为，侵犯了原告的平等受教育权。教育部在该行政行为中，根据不同地域范围对招生人数做了不同的限定，这种限定使得不同地域的考生被划分成了高低不同的等级，并在这不同的等级中参加高考。等级之间分数标准差异巨大，从而直接侵犯了包括原告在内的广大考生的平等受教育权。

考生自述

考生张天珠指出，以北京为例，今年北京的文科重点控制线是454分，而青岛却是580分，相差达120多分！整个北京市今年高考录取比例是1.43：1，而山东省的比例是2.2：1。按照这个比例推算，山东一个孩子考上大学，要比北京的孩子多付出50％的努力。

提起诉讼的三名女生的分数分别是：姜妍522分（理科）、栾倩457分（文科）、张天珠506分（文科），这样的分数，在北京完全可以考上较为理想的大学，而在青岛，情况却是另外一回事了。

选稿:邱曙东

来源:青年报

## \[2020-08-09] 三yellow片

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-565fa6662317132d40544f1367c1605b1f56991e%2F2020-08-09-18-59-29.png?alt=media)

## \[2020-07-19] 404的国度

我们随意打开一篇被和谐的文章，例如：

<https://mp.weixin.qq.com/s/gpc_8WbBVP7bQFEH2LOiyw>

显示：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-652d4e41ad44e5baea1d49ef858d8a4a363897af%2F2020-07-19-11-41-36.png?alt=media)

点击[详细内容](http://www.cac.gov.cn/2017-09/07/m_1121624269.htm)

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4712ec203869052ddd38acdbcf59d99bb58033a9%2F2020-07-19-11-43-02.png?alt=media)

如果政府说过的话都不想认了，那我真的没话可说了，希望是因为网站调整造成的链接失效吧。

## \[2020-07-18] 上海上个学太难了

摘自： [85个家庭质问六师附小：诱骗家长，暴力相向，谁给你的嚣张？](https://zhuanlan.zhihu.com/p/161340580)

上海市有非常庞杂的上学制度，这个制度建立在房产与户籍制度基础上，而一个家庭是否能满足这两个制度，是能让自己孩子在上海上个好小学的重要条件。

上海市把所有在沪家庭分为5类12等，等级越高，被录取时越会被优先考虑。

分类如下，且每一类也会根据是否离婚、是否为上海户口、是否有房产等划分为不同等级，而每一等级又会根据房产购置年头再划分为数个等级：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-5326bd45c58bb48d298d4220a25b75755a7e01e8%2F2020-07-18-16-50-21.png?alt=media)

所谓第一类“人户一致”，就是你拥有上海的户口，又有上海的房产，如果刚好你想让孩子去上的小学，又对口你的户口所在地区，那你就满足人户一致政策。

如果你所在地区没有优质小学，你想让孩子去上海其他地区上小学，或者说你没有上海户籍，但是有房产，那你就掉到第二梯队里，变成第二类“人户分离”。

但即便你有上海户口，如果你没有房产，你也只属于第三梯队，各位可以根据上面的细则看看自己假如在上海，究竟可以被划分为哪一等家庭。

为什么说非本地人上学这么难？主要与另外相关的3项政策有关：

非沪籍如何变成沪籍的政策，居住证120分积分政策，非沪籍如何在上海购房的政策。

这3个政策都有很高的门槛，导致上海市出现了一个魔幻的现象：很多家长为了孩子能够上学，而不得不回炉重造，和孩子一起继续求学。因为更高的学历可以获得更多的积分，哪怕拿不到沪籍，也可以拿到120积分。

满足上面“拥有沪籍、居住证满120分、在上海购置房产”3个条件中的任何一个，都是非沪籍者在上海的巨大阶级跃升。

同时，上海许多家长也把上海的学校分为3大梯队，他们挤破了头，只想让自己的孩子能够跃升一级。

很多人说高考是一场沉重的厮杀场，又有人说，在上海，中考才是最残酷的筛子，一半多的人甚至无法上高中，但很多人忽略了，这种厮杀，在小学就已经非常激烈了。

针对上海市小学的升学状况，有媒体曾作出统计，将上海的小学粗略分成三大类，可以得出如下的数据图：

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-8cb65cb556f57e4fa99fe0598393be6fd17368bf%2F2020-07-18-16-50-51.png?alt=media)

我们可以明显看出，最优质的的小学大部分都被民办占据，公办只占很小一部分。而排在底部的菜场小学，连上高中的比例都只能达到5%。

菜场小学这一称呼最早就发源于上海，因为上学没门槛著称，外来务工人员子女就读占很大比例，学校教学进度会拉慢，教学质量也会参差不齐。

但民办同时意味着高昂的学费，稍好一点点的小学学费都要1万以上，他们根本都上不了民办小学的榜单，在2019年公布的上海市民办小学学费排行榜里，学费一年最高达到16万。

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M6TU8XK0hd3CZdz_gQ_%2Fuploads%2Fgit-blob-4923333488f5d1e2e94c3d08768d1adb969e1ba0%2F2020-07-18-16-51-03.png?alt=media)

## \[2020-07-05] 明史-王振

王振这个死太监做了很多坏事，大明差点就毁在这个太监手里。

最让我感叹的，是其实朱元璋早在50多年前，就定下了内臣不得干预朝政的规矩，就是为了防止这个情况。 而且怕子孙忘记，用铁铸了一个碑，立在宫内。 王振居然让人把它搬走了……第二天早朝，文武百官都发现了，但是没有人敢评论。

这让我想起了一个人……

## \[2020-07-05] 中国的高考公平吗？

[就大学录取模式而言，美国比中国更公平吗？ - 钮祜禄·狗懒子的回答 - 知乎](https://www.zhihu.com/question/32015166/answer/1311712533)

**黑色幽默：一北京人对一山东人说：美国大学录取模式歧视亚裔，不如国内高考公平。**

按照前面的大多数回答，亚裔要比非裔美国人普遍多考100+分才能站在一个录取线上，我们自信一点，假设亚裔必须要多考200-300分，那么200-300/2400(旧SAT总分)=8.3%-12.5%，平均一下10% 而根据2001年著名的山东考生状告教育部事件，当时高考还是全国一张卷，分省录取。北京文科重点本科录取分数线为454分，一般本科429分，专科360分；山东文科分数线则夸张得有些离谱，重点本科录取分数线为580分，一般本科539分，专科为509分。也就是说你如果是山东考生，想考重本，不公平率就是150/750(高考总分)=20%。 是在美国身为亚裔比非裔多考出10%上UCLA，还是留在山东比北京人多考20%上山东理工，一目了然。 至于上面某曹大V认为中国高考就是让没考上的人服，我特想问问山东河北河南江苏的同学，你们服么？

评论区很多人对山东560分上专科这件事感觉不可思议，认定人家是编故事。 户籍限制了你们的想象力，不是你们的错。 我们摆事实讲道理，别我朋友我亲戚我大爷他外甥这种无凭无据的，我们就拿最近一个热乎乎的新闻来看：山东冠县人陈秋媛（化名）高考“落榜”16年后，发现被冒名顶替上大学

![](https://3247607006-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6TU8XK0hd3CZdz_gQ_%2Fsync%2Fa57d3c5f37d3aec5e9b47aa91e303675b1461e28.png?generation=1594126930602278\&alt=media)

2004的546分被山东理工大学的专科录取，多么稀松平常的一件事情。平常到陈秋媛觉得这分数上不了山东理工的大专也是情理之中的，所以没有深究。 而2004年的理科546分在北京能上什么档次的学校呢，这我先给忽略掉北京卷因为过于简单可能产生的通货膨胀，北京理科一本线491，山东理科专科线519，我们首先知道陈秋媛在北京考个一本难度不大。

再看学校，嗯，扣除试卷过易造成的通货膨胀，山东546分的水平上个北邮没问题，最差北林兜底。 公平，真tm公平。 偷走陈秋媛人生的又何止只有顶替她上大学的那一家人，被偷走了还要被问一句：何不上一本？努力学习还只能上大专的都是智力有问题哦。

每个学校对各省统一录取率，清北每年在北京考生里招0.01%的人，就在山东考生里招0.01%，就在西藏考生里招0.01%，虽然这样无法阻止高考移民继续向北京上海西藏青海的户口进发，但至少可以做到大多数在同省受教育的考生被给予的机会都差不多。 很难做到么？ 嗯，想想还真的很难，毕竟动了各方老爷的政绩和利益，还是牺牲一下这些家庭综(hu)合(ji)实力不行考生，让你们互掐去吧。 再说了，谁叫你们父母不努力，人家祖上三代人拼命拿北京户口的奋斗，就这么被你一山东穷学生12年的寒窗苦读给超越了？凭什么？人生是一场接力赛，不是短跑哦，么么哒。

## \[2020-06-04] 力学硕士的私活

[全国青少年科技大赛获奖作品的真实性如何？如何看待网爆部分家长将科研成果安到孩子身上的现象？ - 春时粟的回答 - 知乎](https://www.zhihu.com/question/398596499/answer/1259258741)

哈哈哈，这种事情太普遍了，只是这次爆出来而已。 本人就曾经替某领导的闺女做过科研项目，参加了竞赛发表了论文，人家闺女很轻松的就上了好大学。 本人985毕业，几年前在某单位工作。 一开始工作比较闲，处于学东西的阶段嘛。 有一天我们主任神秘的找到我，把我叫到办公室外面的楼梯口，皱着眉头， 说有个任务交给你，你一个人办就行了，别给其他人说。 我当时就觉得是不是中央有指示，或者突然某个亲戚当上了大官？ 我们主任拿出一个U盘，说这是一个小课题，里面有具体的任务，做完后你整理个报告出来，看你文章发的不错，就按科技论文的格式写就行了，中英文各要一个。 ‘你在自己电脑上操作下，不用来上班了，回宿舍干活去吧。’ 我十分重视，屁颠屁颠的就开干。 一个很简单的课题，应该是空气污染物在密集建筑中的传播，大体的研究内容里面都写好了，条目很清楚，研究步骤也很全，有计算有试验，如果不是这么low的课题说不定真能发篇期刊论文。 试验部分课题书里面已经有人做了，具体就是模拟建筑，生成烟雾，视频录像烟雾的分布。不难，不过已经有科研训练的雏形了。 我要做的是数值模拟一下空气流经这些建筑的流场，说明建筑不同密度对于气流速度的影响，佐证试验里烟雾的流动是否跟计算吻合。 很简单，画个网格算一下就行，一个上午就搞定。像我等硕士搞这个就是大材小用啊，我十分自负。然后开始写报告，整理成英文弄好。 忐忑的去跟主任交差，谦虚的说没算太好，大体能对上。 主任从包里掏出200块钱，说是酬劳，我收起钱，多问了一句： ‘我们做任务都是给现金吗？’ 我们主任可能那天心情不太好，絮絮叨叨的开始描述事情全貌。 这个任务是上边领导交给他的，他要面子，感觉自己没法干，也就找我这小年轻了。 谁交给领导的呢，貌似是某局官员，孩子参加课外实践，正好想参加全国青少年什么竞赛，优秀者还能去报选国外那边组织的一个奖项。 正好我们就是搞这一行的，领导就自告奋勇说能办，然后从人家闺女那接了个任务，还特么免费的。别人家女儿是甲方，我们这么大单位是乙方，哎，找谁说事去。 回来分解，试验是另一个部门的小伙做的，摄像机用的高清仪器，模拟我来算。 钱是哪来的？领导自己掏的腰包呗。 **然后我感到深深的恐惧，我们这种985硕博士，也就配给公务员大人的女儿写份课外实践报告。而他们的女儿，还能拿着报告参加各种奖项，再凭奖再把我们普通家庭的孩子干掉。** 这世道怎么这么残酷，要不是那200块钱，我当场就想哭一场。 后来听说那个报告果然获得了一个国际竞赛的二等奖，人家官员的闺女也很轻松的上了中国某top文科大学。 我在单位搬砖，唯一获得就是200块钱，后来买了几个肉夹馍含着泪吃了下去。


