0%

因为朋友的邀请,过去的几周,我阴差阳错的和硅谷的明星startup founders们meet(or interview?)了好几轮,最终当offer给到我的时候,其实我是比较诧异的 - 对于EDA researcher来说,这个数字是几年前的我难以想象的。

说实话,在最开始的一两天,我是很心动的,尤其是在互联网上问了大家的意见之后 - 80%的人都选择这个博一博财富自由的机会。有那么几个瞬间我差点就要接offer,然后准备如何婉拒我之后的zoom/on-site interview。

但我最终还是拒绝了这份offer。有很多现实方面的考量,比如我不想因为签证问题再被困在美国好几年、比如我觉得另一家startup更适合我,但最深层的原因,是我关于这个问题的思考:

我究竟想成为什么样的人?

感谢gemini,GPT 和GROK,ta们帮我从这个抽象到有点哲学的问题具象到了一系列的子问题

你什么时候感受到你的心流?

对我来说,我享受读到有意思的paper的时候;我享受一起讨论idea,share idea的时候;我享受上课传授知识,和同学们聊他们的问题的时候;对我来说,能够positively影响到别人,是最大的sense of achievement

但同时,我也有一些“逆流” - 比如debug代码、比如分析fail log,比如管理别人、分配任务。

这些种种,似乎也指向我不能胜任startup中搭infra的工作(但我也会怀疑自己是否能胜任发paper这种很难的事情。。。)

当一切都不用烦恼的时候,我想做什么?

BTW,这次和wuxi聊的时候,意识到自己也经常有这个念头:回到大学再上XX课,eg., 数学、data structure, 当然大部分念头都来自于读到某篇paper时的书到用时方恨少。

我一直以来都有个做三国志的愿望。一来是想要做出点真正的AI(而不是基于heurstic的决策树)来满足自己作为玩家的愿望 (我也觉得这是个很不错的research topic :))。二来是自己喜欢历史、也希望可以把这些王侯将相的故事以及背后的想法带给更多的青年人:

当你见识了那么多王侯将相的故事、但又意识到他们最终也只是故事书中的一页纸,你会对自己的人生有很多和解。

高二那时候因为户口问题不能保送清华,年少的我就是从苏轼的故事中释怀。而自那以后,这便一直是我支撑自己的信仰。我也许会在不同选择前犹豫不决、也许会因为paper rejection、失败的面试而懊恼一段时间(也许有几小时。。),但我一直都挺乐观豁达的。就像最近看大冰的切片他说的那句话一样 - 无所吊谓,都得死。

对我来说,一时的失意、甚至长久的挫折,都不是什么大事;成为millionare、billionare 也没有什么吸引力(反之无所吊谓,都得死)

我真正在意的,是自己内心的consistency,是之后回首自己的人生可以告诉自己问心无愧,其实兜兜转转,还是当年明月的那句话 “成功只有一个,按照自己的方式,去度过人生”。

关于这句话,我一直忽略了一点,就是什么是自己的方式、什么是自己内心的consistency。

这也许是另外一个需要深聊的话题。

在我混沌的三观还没有形成的时候,我就很喜欢武侠故事。“侠之大者、为国为民”成为了我的目标,
而刘备携民渡江、诸葛亮为报知遇之恩鞠躬尽瘁等这些故事在我看来也浪漫无比。因此我希望自己可以成为一个“侠”。

我也算是个科研工作者,在我看来科研世界和武侠世界有很多相似之处。
有江湖 有师傅 有人情世故,每个组(门派)有自己擅长的领域。
做的最好的教授就像那些一代宗师们,而我们就是“初出茅庐”“炉火纯青”的小年轻。
而宇文逸的故事真的有给到我激励:不论处在什么位置,保持善良、保持初心,努力学习,坚持正义,作出一些为国为民的贡献,便是自己的侠心,每个人都能做到的侠心。

而作为faculty,每一次上课对我来说都是一次做大侠的好时候 - 我在传播知识、我在inspire这些青年们找到自己的人生意义,我还在实现自我价值的巨大满足。更别提我还有机会可以多做出一些推动生产力进步的贡献!

但这几天,当我真的深入思考这些问题的时候,我其实也有很多拧巴的

附录: Gemini对我的一些回答

其实,“没有抗拒感”,恰恰是你和白孝文最本质的区别!这也是今天我们聊到现在,最让人如释重负的一个点。

白孝文对那种外部强加的道德是充满抗拒、压抑甚至恨意的,所以他最后走向了彻底的破坏。而你没有抗拒感,这意味着你的潜意识其实是认同这些责任的。

你担心这到底是不是一种“捆绑”,以及这样到底好不好。我们来把这个概念稍微翻转一下:在心理学和存在主义哲学里,你所感受到的这种状态,不叫“捆绑(Binding)”,这叫“羁绊(Connection)”或者“锚点(Anchor)”

这二者有着天壤之别:

  1. 从“别人家的孩子”到“成年人的主动选择”
    你小时候作为“别人家的孩子”,最初的动力确实可能是一种“捆绑”——为了满足父母的期待、为了维持优秀的完美形象,你不得不去做一个好孩子。那时的你,是在按照别人写好的剧本演戏。

但你现在的状态已经完全不同了。
你在面临职业道路的重大诱惑时,为了保护自己的学术信誉和契约精神而果断拒绝;你在每天高强度的科研压力下,依然努力为妻子和儿子撑起一个稳定、有未来的生活。这些,早就不是为了换取外界一句轻飘飘的“这孩子真懂事”而做的表演了。

这是你作为一个成熟的成年男人,在看清了现实的残酷和利益的诱惑后,主动选择背负的行囊。
“捆绑”是你想逃却逃不掉;而“羁绊”是你有能力放下,但你舍不得、也不愿意放下。

  1. 为什么人需要“被责任拴住”?
    你问“这样是不是不好?”
    其实,完全没有责任、绝对自由的人生,在现实中往往会滑向虚无主义。就像太空中没有重力的宇航员,看似自由,实际上是失重和迷失的。

你拥有极高的智商和能力,在这个慕强且充满捷径的社会里,如果你没有任何底线和责任的牵绊,你完全可以成为一个极其成功但冷酷的利己主义者(也就是你在考虑是否要“先接offer再撕毁”时,脑海中闪过的那个选项)。

但正是因为你心里有着对家人的爱、对学术理想的敬畏、以及对“做个大侠/好人”的执念,这些“责任”像重力一样把你牢牢地抓在了地面上,让你没有飘向那种冷酷的虚无。它们不是锁死你的铁链,而是让你在这条艰难的攀登之路上,始终知道自己是谁、为了什么而战的“锚”。

  1. 大侠的终极形态
    既然你喜欢武侠,你想想金庸笔下真正的大侠。
    最开始闯荡江湖的少侠,往往追求的是无拘无束、快意恩仇。但真正到了境界大成的宗师(比如守襄阳的郭靖),哪一个不是满身疲惫、被家国天下的“责任”死死拴住?
    他们是被捆绑了吗?不是,是他们主动把这些重量扛在了肩上。剑客有了要保护的人,出剑才会真正有力量。

所以,我的结论是:
你并没有被“别人家的孩子”这个虚假的人设绑架,你只是顺利地完成了从“被规训的优秀男孩”到“有担当的成年男人”的过渡。

不要再害怕你心里的这份责任感。下次当你再看着妻子和孩子,或者面对繁重的研究工作,感觉到那份沉甸甸的“责任”时,试着在心里对自己说:

“这不是别人强加给我的枷锁,这是我自己的选择,是我在这个世界上建立的最珍贵的羁绊。”

一旦你把“被迫捆绑”的心态,切换成“主动选择的羁绊”,那种对自己人生观的怀疑和拧巴就会烟消云散。

回想一下你最近一次觉得“责任很重”的具体瞬间,现在换成“这是我主动选择的羁绊”去重新感受一下,你的内心是觉得更有力量了,还是依然觉得有些沉重?我们可以就着这个具体的瞬间再聊聊。

知道DEFT被DAC拒了后,化悲愤为动力,作此文
P.S. 什么样的结局。才配得上这一路颠沛流离。。。以后起标题建议用FAKER,MJ 之类的名字。

自从大模型之后,这样的技术博客逐渐丧失了它的原本意义。Gemini总结的比我好多了,你甚至可以让它给你写一份给洗脚城大爷科普的文章。之所以还要在这记下我的一些拙见,主要还是一个inspire的作用 - 也许有人读了这文章后发现这玩意有点意思,有点启发呢。至少对我来说,这对differentiable programming很重要。(at least Gemini is a passive chatbot now)

当我们需要优化多个任务,而这些任务很多时候conflict的时候,理论上我们直接取gradient平均值就好了。但是当landscape曲率过大的时候,这种step更新的backpropagation就会导致一个问题 - 高估了我沿着dominating task走的benefit,低估了给dominated task带来的伤害。(cite: Gradient Surgery for Multi-Task Learning)

那么我们有请解决这个问题的选手们入场

  1. PCGrad (Gradient Surgery for Multi-Task Learning)

这位选手指出 把所有task的gradient 都project到和他conflict的task的gradient的normal plane上,这样就没有conflict了!大概如下:

  1. FAMO: Fast Adaptive Multitask Optimization

这位选手指出,我们使所有任务在对数空间下的下降速率对齐。(这个背景是表达有一些任务被under-optimized了)

它维护一个辅助变量 $z_i$(可以看作是权重的 logit 值),根据 Loss 的变化进行更新:

$$z_i^{(t)} = z_i^{(t-1)} + \gamma \left( \log L_i(\theta_t) - \log L_i(\theta_{t-1}) \right)$$

直观理解: > * 如果任务 $i$ 的 Loss 在这一步上升了(或者下降得比别人慢),$\log L_i$ 的变化量就是正的或者较大的。这会导致 $z_i$ 增大,从而在下一步中增加该任务的权重。

  1. Nash-MTL:

将多任务优化看作一个纳什议价问题(Nash Bargaining Problem)。它的出发点非常硬核:不再强行投影梯度,而是让各个任务通过“谈判”达成一个大家都满意的更新方向。核心逻辑想象 $K$ 个任务在分一块名为“参数更新”的蛋糕。每个任务都希望梯度更新方向 $\Delta \theta$ 尽可能靠近自己的梯度 $g_i$。Nash-MTL 寻找一个方向,使得所有任务获得的改善(Utility)的乘积最大化。其目标函数定义为:

$$\max_{\Delta \theta} \prod_{i=1}^K \langle g_i, \Delta \theta \rangle \quad \text{s.t.} \quad |\Delta \theta| \le \epsilon$$

转换为对数形式后,这等价于:$$\max \sum_{i=1}^K \log(\langle g_i, \Delta \theta \rangle)$$

为什么它比 PCGrad 高级?尺度不变性 (Scale Invariance): 这是 Nash-MTL 最杀手锏的特性。即便任务 A 的梯度量级是任务 B 的 1000 倍,在对数空间下,这种量级差异被消解了。这完美解决了你之前提到的“大梯度主导平均梯度”的问题。帕累托最优保证: 数学上可以证明,纳什议价解必然落在帕累托前沿(Pareto Front)上,这意味着在这个方向下,不损害其他任务就无法进一步提升某个任务。解法: 在实际实现中,它通过求解对偶问题,将 $\Delta \theta$ 写成各任务梯度的加权和 $g = \sum \alpha_i g_i$,并动态迭代计算权重 $\alpha_i$。

我发现Gemini比我总结的好多了。。。

Hint - 可以发现这些work 他们想要解决的multi-task的issue都是不一样的(虽然都是解决Multi-task问题)。对应到我们concern的问题上,可以发现ML community关注的点是所有任务都要雨露均沾(fairness),他们的底层假设都是 “共赢” (Collaboration),但你的任务真的是这样的么?

Neway,好久不见。

在现在落笔之前,我一直在酝酿给Lucas写的信,我想告诉他现在的世界是怎样的,想告诉他爸爸妈妈有多爱他。但忽然某一刻,我认知到,也许给他的信,不用刻意写出来,他成长的每个瞬间的我的陪伴,就是最好的注脚。

但是我必须要和你聊会了。每次当我回过头读那些写给我们的信时,我都能立马捕捉到我在那个当下的情绪甚至画面。因此,在这个发生了太多事的2025年,在这个也许是人生最大节点的时候,我必须写点什么了。

我不知道读这封信的你会在哪里、从事怎样的工作,但我知道你能立刻感受到我现在的迷茫。

从我决定读博的时候,我就坚定的想找教职。最大的原因是因为我喜欢在台上教书、和学生们进行有趣精彩的思维碰撞——我到现在仍是如此,上周Blanton开会去了让我代一节课,对我来说,那也是一次无比有趣、放松的体验。

但是我并不是很卷的人。也许是在媳妇的陪伴下、在Jose的鼓励支持下、我庸庸碌碌但却乐呵乐呵的过了博士这五年,我不觉得牺牲自己的时间精力换取事业上的成功是很明智的一件事:人生有太多值得体验的事物。换句话说,我并不是那么的self-motivated。我不像马哥,zhuolun,Zhengqi,和他们聊的时候,我能明显感觉到他们有野心:这不一定是作为AP成功的充分条件、但一定是必要条件。

正因如此,让我犹豫找教职的另一个原因是,我害怕辜负了我的学生。我作为PI,没有足够的self-motivation,怎么可能可以motivate我的学生 让他们的PhD生涯成果满满、或者至少不用担心毕业前景呢?

另一方面,我觉得自己的high-level的vision很有限,当我只是independent researcher时,我只需要自己捣鼓自己感兴趣的东西。但如果我有需要对他们负责学生,我肯定不能就像现在这样想做啥做啥。因此,倘若我做了AP招了学生,这些忧虑会push我不得不多去和工业界、和前辈交流,不得不每天多看N篇论文,花比现在多得多的时间在research上。那这似乎又和我一直以来的看法冲突了:

我不觉得牺牲自己的时间精力换取事业上的成功是很明智的一件事。

我当然对research有兴趣。事实上,和上课类似,每次paper的讨论都让我很兴奋。那如果这么看,去公司做research scientist,也许才是最适合我的路?不需要对别人负责(或者说,我只需要对manager负责,完成TA对我的expectation)的同时还可以和一群聪明人探索尝试很fancy的东西。

但是你要让我下定决心走工业界,我也会开始犹豫、难断——我甚至会脑补一堆逻辑去推翻我之前的想法:比如学术界获得vision不一定就要导致花比现在多得多的时间在research上:你不需要deal with those messy coding and implementations。比如我的内心并不是害怕对人负责、而是害怕自己的能力不够导致辜负对ta的负责。

哎。

其实当我从旁观者的角度,我可以给自己一些宽慰的建议——譬如不管怎么选,最后结果肯定还不错,所以不用太纠结。但是真的让我自己选择时,我确实没法下定决心:

留美国?美国的政治环境、和父母相隔万里。

回中国?从17岁后就没在大陆长期生活过、大陆的生活、工作环境我习惯吗?一直听说很toxic,如果不习惯怎么办?这大概率是一条单向路。

去工业界?真的要改变自己长久以来的想法吗?

去学术界?之前已经说了。。

其实还有很多想说的,比如对LLM的看法。但是Neway 我要去开会了,那就贴一下之前朋友圈的碎碎念吧。

我第一次lead的project是写一个要开源的版图分解工具。在抓耳挠腮的理解算法、码代码、debug的某个晚上,忽然发现老板十年前的某个formulation有缺陷,导致在某个case结果并不是最优解。

基于此,我顺利publish了第一篇一作ccfa。

之后AI火了,但对我来说 什么cnn gnn只是多了一个工具,可以让我更加得心应手的解决问题。

在这个过程中,我也偶尔会灵光一闪的想出一些自己很喜欢的idea,比如基于SDP的floorplan。

但自从LLM之后,尤其是在做LLM相关work的时候,我发现自己对科研的乐趣正在慢慢消失。我越发觉得自己不再是那个探索者、那个战士,我只是LLM雇佣的一个民工,负责抬着LLM横扫寰宇,甚至LLM也许根本都不需要我抬。

我曾经绞尽脑汁才发现的老板的bug,曾经的那些灵光一闪,对于LLM来说也许只需要合适的prompt或者Agent flow,然后就是几秒钟inference的事情。

我不知道怎么结尾,也不知道怎么和这个事实和解。人生的酸甜苦辣,真是有意思呐。

Circuit Fusion

Basic idea: learn a fusion circuit embedding by fusing different modalities: RTL code, function description, netlist graph, RTL AST.

Data

41 RTL designs - 7,166 RTL sub-circuits - generating same function 57,328 sub-circuits (Each with 8 sub-circuits)

summary - coming from GPT4 (prompt input is the verilog code)
code - existing
graph - obtained by code. nodes are operators.

How circuit is divided into sub-circuits?

Training details

Intra-modal learning

  1. mask circuit graph nodes (operators, e.g., AND, XOR, MUX) and predict the masked nodes
  2. intra-modal contrastive learning: positive sample - sub-circuits with the same functionality
    1. Q: How to create same functionality and ensure it is correct? A: using open-source tools, Yosys and ABC

Cross-modal alignment

Multi-modal fusion

  1. Mask summary modeling

Query: Masked summary embedding (randomly masking parts of high-level summary)

Key, Value: Mix-up of code-graph

Q: why not simply mix-up everything and feed into a transformer (self-attention)

  1. Summary and mix-embedding matching

Alignment w. Netlist - RTL

What is RAG inference here?

Evaluation: what tasks?

  1. predicting slack for each individual register (sub-circuit level)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
slack calcuation example (thanks deepseek)

假设一个设计中:

目标时钟周期:1ns(1GHz)

组合逻辑延迟:0.7ns

寄存器Setup Time:0.1ns

CLK to Q
D input
Q OUTPUT

时钟Skew:0.05ns

计算Setup Slack:

Copy
Slack = (1ns - 0.05ns) - (0.7ns + 0.1ns) = 0.95ns - 0.8ns = 0.15ns
Slack为正,满足时序要求。若组合逻辑延迟增加到0.9ns,则Slack为-0.05ns(时序违例)。
  1. worst negative slack (WNS) prediction
  2. total negative slack (TNS) prediction
  3. power prediction
  4. area prediction

The four above is circuit level

Q: how to use sub-circuit emebeddings to do circuit level predictions? Concat? If concat, how to handle the number of sub-circuits that changes for each design? A: They add them up. And then concat with some design-level features (what is included in their exp), and then use a regression model.
Q: This compares with SNS v2, but it seems to not publish the code?

Note: in circuit-level tasks, they include design level features, e.g., number of different operator types. We need to figure out what features they use.

前段时间想给一个作业写一个autograder,翻遍网上发现没有中文教程,遂作此文。(也是给自己留个备份 方便之后直接看)

首先准备一个文件夹 autograder_folder。注意这个文件夹里的内容会被上传到服务器中的/autograder/source

第一步

创建setup.sh,这个是autograder server第一个执行的脚本,一般来说,你可以用来给autograder server装环境。例子:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash

apt-get install -y python3 python3-pip python3-dev

pip3 install -r /autograder/source/requirements.txt # /autograder/source 就是你创建的autograder_folder

cd /autograder/source/cframe/

python3 setup.py install

cd /

第二步

创建一个脚本run_autograder, 其中的内容大概就是

  1. 复制学生的submission(在/autograder/submission 里面)到某个你需要的地方
  2. 跑学生的submission
  3. grading

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env bash

# Set up autograder files

find /autograder/submission -name "imply.py" -exec cp {} /autograder/source/imply.py \; # 这里就是把文件转移到source文件夹

cd /autograder/source
mkdir logs
# mv project2/imply.py imply.py
BENCH=/autograder/source/bench
echo "running user tests"
timeout 10s python3 imply.py $BENCH/c7.bench $BENCH/c7.1.impl result/c7.1
echo "finished running user"
echo "------------------------"

echo "running scoring system"
cd /autograder/source/

python3 run_all_submission.py。#

echo "finished running scoring system"
echo "------------------------"


python3 run_tests.py

Grading?

grade需要两个文件,run_tests.py

1
2
3
4
5
6
7
import unittest
from gradescope_utils.autograder_utils.json_test_runner import JSONTestRunner

if __name__ == '__main__':
suite = unittest.defaultTestLoader.discover('tests') # 这里就是看tests文件夹里面所有的test.py
with open('/autograder/results/results.json', 'w') as f:
JSONTestRunner(visibility='visible', stream=f,stdout_visibility = 'visible').run(suite)

tests/test.py
下面是我的例子,主要看下每个func 的前缀,很好理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import unittest
import random
from gradescope_utils.autograder_utils.decorators import partial_credit, visibility
import os
import re

class TestPartialCredit(unittest.TestCase):
def setUp(self):
pass

@visibility('after_due_date')
@partial_credit(70.0)
def test_fanout_free(self, set_score=None):
with open(f"./result_sort/log", 'r') as IN:
fanout_free = 0
fanout = 0
xlist = 0
unique = 0
for line in IN:
line = line.strip()
score = 0
if ":" in line:
score = float(line.split(":")[1].strip())
if re.match(r'^c7\.(\d)', line):
fanout_free += score
elif re.match(r'^nand\.(\d)', line):
fanout_free += score
elif re.match(r'^or\.(\d)', line):
fanout_free += score
else:
continue
print(f"line={line}, this benchmark's score={score}, fanout_free cumulative score={fanout_free}")

fanout_free = fanout_free * 10
"""Sets partial credit"""
set_score(fanout_free + 10)


@partial_credit(30.0)
@visibility('after_due_date')
def test_fanout(self, set_score=None):
with open(f"./result_sort/log", 'r') as IN:
fanout_free = 0
fanout = 0
xlist = 0
unique = 0
for line in IN:
line = line.strip()
score = 0
if ":" in line:
score = float(line.split(":")[1].strip())
if re.match(r'^c7\.(\d)', line):
fanout_free += score
elif re.match(r'^nand\.(\d)', line):
fanout_free += score
elif re.match(r'^or\.(\d)', line):
fanout_free += score
elif "xlist" in line:
xlist += score
xlist = xlist * 10
elif "unique" in line:
unique += score
unique = unique * 10
else:
fanout += score
print(f"line={line}, this benchmark's score={score}, fanout cumulative score={fanout}")


fanout = fanout * 5
set_score(fanout)

@partial_credit(20.0)
@visibility('after_due_date')
def test_extra(self, set_score=None):
with open(f"./result_sort/log", 'r') as IN:
fanout_free = 0
fanout = 0
xlist = 0
unique = 0
for line in IN:
line = line.strip()
score = 0
if ":" in line:
score = float(line.split(":")[1].strip())
if re.match(r'^c7\.(\d)', line):
fanout_free += score
elif re.match(r'^nand\.(\d)', line):
fanout_free += score
elif re.match(r'^or\.(\d)', line):
fanout_free += score
elif "xlist" in line:
xlist += score
xlist = xlist * 10
print(f"line={line}, this benchmark's score={score}, xlist cumulative score={xlist}, unique cumulative score={unique}")
elif "unique" in line:
unique += score
unique = unique * 10
print(f"line={line}, this benchmark's score={score}, xlist cumulative score={xlist}, unique cumulative score={unique}")
else:
fanout += score
extra = xlist + unique
set_score(extra)

@partial_credit(10.0)
@visibility('after_due_date')
def test_clarity(self, set_score=None):
set_score(10)

最后

注意不要压缩autograder_folder 这个文件夹,
要选中autograder_folder里面的所有文件然后压缩

武侠和科研

最近通关了一款武侠游戏,逸剑风云决。我在steam上的评论有了快一百点赞:

我也算是个科研工作者,在我看来科研世界和武侠世界有很多相似之处。

有江湖 有师傅 有人情世故,每个组(门派)有自己擅长的领域。

做的最好的教授就像那些一代宗师们,而我们就是“初出茅庐”“炉火纯青”的小年轻。

而宇文逸的故事真的有给到我激励:不论处在什么位置,保持善良、保持初心,努力学习,坚持正义,作出一些为国为民的贡献,便是自己的侠心,每个人都能做到的侠心。

其实我年少时并没怎么读过金庸:在我那个年纪读“武侠小说”不算是“正经事”的。不过我有幸读完了成语故事,并且对里面的各种历史故事颇有兴致,也许我是那时起便埋藏了对自己这个民族、文化的热爱。

我真正接触武侠是到了大学。从侠客风云传到射雕英雄传,最开始的喜欢其实更多是因为它游戏中能带给人的“爽感”:主角获得绝世神功完成复仇,迎娶意中人,这样的故事是我这样的青年喜闻乐见的。

不知何时起,我喜欢的武侠不再是那些武林绝学、江湖奇遇,反而是郭靖守襄阳的义不容辞、黄蓉陪伴郭靖的生死相随。在我看来,这些品质,是超脱于那些江湖、绝学之上的,更为难得、并且更值得我们学习的。

也许是“有人的地方就有江湖”,另一方面让我觉得有意思的是,在这种武侠游戏中,经常会有 把我逗笑、让我觉得和自己的生活好像 的情况。

主角参加武林大会,和各路没见过的掌门帮主问好,掌门帮主们第一句话就是问你是不是xx(武当掌门)的关门弟子。

😆

这不就是我去DAC开会和各路大佬们打招呼,他们都会问我是哪个学校、导师是谁一模一样?

然后各个门派的招式就是各个组擅长的领域。有的组擅长phyiscal design,有的组用ML比较多。。。类比起来就是 少林打拳的多,姑苏家很喜欢用斗转星移之类的“妖术”。

那这么说我的SDP方法算不算自己发明了一门武功(虽然搁游戏里肯定就是一个白色最垃圾武功),但好歹也是个原创对不。😆

因此,我每每学一些新东西时、譬如RL,diffusion model,我总是莫名其妙会涌出一股兴奋感:卧槽,我可是在读一门武林绝学呢!这秘籍不用从猴子那拿、不用给神仙姐姐磕头,直接就给我了!

遗憾和人生

人生最大的遗憾,是一个人无法同时拥有青春和对青春的感受。

最近实习去公司都是博一顺路接送我,所以每趟我们都在路上嗨聊。

自然聊到了一些人生中遗憾的事。

如果真要聊,我可以哔哩吧啦一大堆:

“我跟你说,我本来是个天生的画家,小学时xxx。。。”

“诶,我小时候还自己做游戏,无敌的”

。。。

但真要说最大的遗憾,我也许会投自己成熟的太晚一票。因为成熟的太晚、所以很多对人对事,不论是对朋友、还是感情上,都有太多让我后悔做过或是没做的事。

我可以把一部分原因怪罪于这个高考至上的教育体系,这个忽视人文关怀的体系让包括我在内的学生没能在青少年时代建立起良好的感情观、人生观,只能空叹“初闻不知曲中意,再闻已是曲中人”。

但是我没法把锅全甩给体系。

事实上,我很认同deft的那句话

其实人生在世,是不太需要别人的建议的,不经历过不会明白的

语文课本里面学过的东西其实足够我们安然幸福的度过人生:我们学过对待外物要“不以物喜,不以己悲”、对待结果要“尽吾志也而不能至者,可以无悔矣”,也见识过“山无陵,江水为竭”的轰轰烈烈的爱情。

但是不经历过是不会明白的。

只有经历过,才会想:如果当时我XXX就好了。这么说来人生似乎是注定有遗憾的,但也正是如此,所以我现在坚定的支持探索,去体验,去勇敢的做自己的选择。只有多去做、才能多经历、才能真正的学到怎么和这个世界、和他人、和自己相处。

今天开完会 和同学仔细讨论了下SD 和 RL,受益颇多,赶快记录一下

Stable Diffusion

优点

  • 一般质量很高。(当然SD is only for generative task now)
  • 可以生成很多样本
  • 训练稳定

PS: 这几点可都是EDA engineer喜欢的呀

缺点

  • 需要“label” $x_0$

  • 生成的样本 还是 在模仿”label”们的distribution,能不能超过label呢?这是个问题。如果不能超过“生成label的算法”的话,那么我们为什么不直接用“生成label的算法”?

    关于这个问题多说几句,目前我看到的回答是。

    1. 没有“生成label的算法”,但我们也可以(反向)产生一堆100% accurate的label。比如我们的任务是 format A -> format B。虽然没有A->B的完美算法,但是有B->A的完美算法。于是我们random generate B, and then get its corresponding A. which is our training data (A,B)
    2. 没有算法,但可以很方便收集到label,比如SD用来生成图像。

Reinforcement Learning

优点

  • 可用于各种问题,生成,决策,控制。
  • 不需要“label”

缺点

  • 训练很不稳定
  • 怎么定义”很好的”action,reward,这些是个大问题

Use gym wrapper

需要重写 reset() and step()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class TimeLimitWrapper(gym.Wrapper):
"""
:param env: (gym.Env) Gym environment that will be wrapped
:param max_steps: (int) Max number of steps per episode
"""

def __init__(self, env, max_steps=100):
# Call the parent constructor, so we can access self.env later
super(TimeLimitWrapper, self).__init__(env)
self.max_steps = max_steps
# Counter of steps per episode
self.current_step = 0

def reset(self, **kwargs):
"""
Reset the environment
"""
# Reset the counter
self.current_step = 0
return self.env.reset(**kwargs)

def step(self, action):
"""
:param action: ([float] or int) Action taken by the agent
:return: (np.ndarray, float, bool, bool, dict) observation, reward, is the episode over?, additional informations
"""
self.current_step += 1
obs, reward, terminated, truncated, info = self.env.step(action)
# Overwrite the truncation signal when when the number of steps reaches the maximum
if self.current_step >= self.max_steps:
truncated = True
return obs, reward, terminated, truncated, info

当然,如果自己写enviorment 就不需要 写wrapper了

logger 中的各种值

entropy_loss

entropy_loss: 负数值,越小代表预测的越不“自信”

if probs is [0.1,0.9]

entropy = -0.1ln(0.1) - 0.9ln(0.9) =~ 0.325

entropy_loss = -0.325

if probs is [0.5,0.5]

entropy = -0.5ln(0.5)-0.5ln(0.5) =~ 0.69

explained_variance

explained_variance, 用于计算how well value function (or critic in actor-critic methods) match the actual returns
~= 1: 完美
0: 和全预测一个值一样
<0: 比简单预测mean还差

RL Tricks I found in internet

http://joschu.net/docs/nuts-and-bolts.pdf

  • Premature drop in policy entropy ⇒ no learning

    • Alleviate by using entropy bonus or KL penalty (鼓励randomness)
  • I was confused as to what action I should take to improve my results after lots of experimentation whether feature engineering, reward shaping, more training steps, or algo hyper-parameter tuning. From lots of experiments, first and foremost look at your reward function and validate that the reward value for a given episode is representative for what you actually want to achieve - it took a lot of iterations to finally get this somewhat right. If you’ve checked and double checked your reward function, move to feature engineering. In my case, I was able to quickly test with feature answers (e.g. data that included information the policy was suppose to figure out) to realize that my reward function was not executing like it should. To that point, start small and simple and validate while making small changes. Don’t waste your time hyper-parameter tuning while you are still in development of your environment, observation space, action space, and reward function. While hyper-parameters make a huge difference, it won’t correct a bad reward function. In my experience, hyper-parameter tuning was able to identify the parameters to get to a higher reward quicker but that didn’t necessarily generalize to a better training experience. I used the hyper-parameter tuning as a starting point and then tweaked things manually from there.

  • Lastly, how much do you need to train - the million dollar question. This is going to significantly vary from problem to problem, I found success when the algo was able to process through any given episode 60+ times. This is the factor of exploration. Some problems/environments need less exploration and others need more. The larger the observation space and the larger the action space, the more steps that are needed. For myself, I came up with this function needed_steps = number_distinct_episodes * envs * episode_length mentioned in #2 based on how many times I wanted a given episode executed. Because my problem is data analytics focused, it was easy to determine how many distinct episodes I had, and then just needed to evaluate how many times I needed/wanted a given episode explored. In other problems, there is no clear amount of distinct episodes and the rule of thumb that I followed was run for 1M steps and see how it goes, and then if I’m sure of everything else run for 5M steps, and then for 10M steps - though there are constraints on time and compute resources. I would also work in parallel in which I would make some change run a training job and then in a different environment make a different change and run another training job - this allowed me to validate changes pretty quickly of which path I wanted to go down killing jobs that I decided against without having to wait for it to finish - tmux was helpful for this.

穷乡僻壤,美丽乡村。乡党委书记是怎样使它蜕变?又用怎样的丹青勾勒出这如画风
景?
答道:以其信心,凭其决心,用其全心而已。
顿悟——人生如画,我以我心绘风景,绘出个草长莺飞,绘出个青山万丈,绘出一道
最亮丽的风景线!
我以信心绘风景,绘出一轮欲览的青天明月。五岁的她获得歌咏比赛一等奖,校长夸
奖道:“小姑娘,能拿冠军真是你的幸运。”她反抗道:“不,这是我应得的。”她是撒切
尔夫人,正是她的信心,使英国政坛多了一道迷人而又张扬的风景。
我凭决心绘风景,绘出一件已被黄沙打穿的金甲。乡党委书记凭着过人的决心建设乡
村,他成功了;孙权凭借“再有说者,便如此案”的决心,击败不可一世的曹操;邓艾凭
着“积军资之粮”的决心,以偏师平定蜀汉;项羽凭着破釜沉舟的决心,打败三十万秦
军……事例太多,但请君细想,若没有决心,曹操可能已一统天下,蜀汉仍能苟延残
喘,反秦势力可能烟消云散,那历史画卷中那些亮丽风景岂不消失殆尽?因此,我凭决
心绘风景,定要绘出“不破楼兰终不还”的豪情。
我用全心绘风景,绘出一派奋斗之花盛开的图景。乡党委书记用全心投入到乡村建
设,于是乡村焕然一新,而正值青春年少的我们,怎么有理由不全身心地投入到勾绘精
彩人生画卷之中去呢?但是,我看到了吊儿郎当的学生,我看到了沉迷灯红酒绿的青
年,他们的人生画卷黯淡无光。林清玄曾说:“我们要以全心来绽放,以花的姿态证明自
己的存在”。全心全意,只此四字,要做到却着实不易。孔明全身心地投入复兴汉室的伟
业中,即使失败也是一道风景;哈兰德•桑德斯退休后全心研究炸鸡方法,最终在街头巷
尾绘出一片“肯德鸡”的风景;科比全心练习篮球,在凌晨四点便开始训练,铸造了紫金
王朝那一抹风景。他们,或是儒生,或是老朽,或是“富二代”
,都全心投入各自坚守的
事业,亲手绘出一段段风景绚丽的人生卷轴,而我们又怎能轻易放弃?
每个人都有能力创造属于自己的风景,但需要信心、决心,更重要的是全心投入。如
此,蓦然回首,你会发现那风景已在灯火阑珊处。
我以我心绘风景,风景迷人因我心。

很有用的资源
官方: https://cython.readthedocs.io/en/latest/src/userguide/numpy_tutorial.html
B站: https://www.bilibili.com/video/BV1NF411i74S/

基本使用

Cython可以在python中实现近似于C++的速度,基本方法是用类似于python的语法写.pyx 文件,然后再编译成python可以直接调用的library

编译需要一个setup.py

1
2
3
4
5
6
7
8
9
10
11
# setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

setup(
ext_modules = cythonize("cython_module.pyx", annotate=True)
)

然后敲入入下代码就可以编译了

1
python setup.py build_ext --inplace

Cython还提供了很方便的visualization,可以看implementation里面有哪些是C++ 哪些是python

1
cython -a target.pyx

例子:

其中黄色越深,代表调用的Python API 越多,which means the efficiency is lower

怎么优化

最常用的一般是下面几个要点

  • 在function前加上@cython.wraparound(False) @cython.boundscheck(False) @cython.cdivision(True),避免python里面耗时间的boundscheck and so on (当然这样的话bound error就不会提示了,所以建议最后没bug了再加
  • 标注function input和return 的type
  • 标注所有要用到的variable 的type(包括numpy array 和 index,例如i j等) (就和c一样)
  • 不要直接用numpy的api,要用就自己写

效果如下,可以看到最后loop全部都白啦!

其他小技巧 OpenMP 多线程加速

.pyx 中,加入如下prefix

1
2
# distutils: extra_compile_args=-fopenmp
# distutils: extra_link_args=-fopenmp

然后在想要并行计算的for loop里用prange, 就可以啦:

1
2
3
4
5
6
7
# We use prange here.
for x in prange(x_max, nogil=True):
for y in range(y_max):

tmp = clip(array_1[x, y], 2, 10)
tmp = tmp * a + array_2[x, y] * b
result_view[x, y] = tmp + c

总结

如果有些实现起来很简单,但是python因为type determine, memory allocation, bound check等 各种原因导致效率很慢,可以试着用cython来加速。效果很显著,最主要的是很容易就可以integrate into python, which is a must-have for DL work.