序言
很有趣的经历,我决定等有空再写,这需要一些时间(是真的太累,实在不想熬夜,回公寓就想睡觉)。这两天连手写的日记都能有两页纸以上,平时一般只会写半页。其实有很多想记录,有可能是我吃太饱(物理意义上),但组织起来并不是那么容易。
总之,天堂确实存在,但似乎并不属于我们。
有一点需要肯定,总是需要接受存在的差异的存在,并接承认别人比我们好的地方,妄自尊大是可悲的。但也没必要妄自菲薄,平等从来都是理想的,有人享福,就有人受苦,这是现实,但这并不阻碍我们对理想的追求。
记一些回去要用的文稿:
9月17日,在opensession上,今年ICANN2024大约接收400篇,接受率不到40,较于往年收录更多但接受率更低,主要也是因为这是最近四年第一次线下举办。open session后,Jurgen教授进行了keynote speech,不过这更像是一个summary,总结了人工智能的发展历史以及未来的热点。我很喜欢他用的一个词AI curiosity,这个我们用的很少,AI应当对它没有见过的东西更有兴趣,其实就是主动学习能力,虽然这还是没有权威的定义,但是现在的填鸭式学习方法一定不是AI的终点,它应该学会发现知识。
之后当日一共有三场汇报,每一场都有4-5个会场在同时进行,我在主会场Aula Magna听阅了关于计算机视觉相关的工作,第一场(11-13点),主要是围绕图像分类和目标检测展开,比较关注的是第四篇西交利物浦大学的利用反事实增强的图像分类饭后发,这是因果推断在CV上很好的应用,虽然他们讲得不是很流畅。来自MALAYA大学使用energy-base模型,做了一个分类模型,感觉是很传统的方法,但是是老师代学生讲(什么神仙老师),也不好说啥。最后一个是中国科学院大学的团队做的使用YOLO作为基座,利用半监督学习方法来指导Agent打DOTA的工作,这个其实去年就听过不少训练Agent玩MOBA游戏的工作,这是很新颖的,并且已有成型可用的公开数据集用于评测,但是MOBA游戏较于一般的棋类比较特殊,它是一个多对多的决策,涉及更多可能的action以及reward设计问题,而且与CV也是有结合的,感觉上目前其实还有很大的提升空间。讲者在最后祝在座各位中秋快乐,并赠送了作为土家族传统的食品作为礼物,他的英文很流利,但不知为什么听不懂其他人的提问,大家对他实验结果中的Direct Label的问题做了很长时间的拉扯,我听明白了,他本来就是做了个半监督学习,所谓Direct Label的结果就是全监督,所以效果比他提出的用teach-student框架训练的模型还要好。
第二场主要是关于CV中的生成建模方法,这是目前流行的研究方向,值得关注的是苏州大学学生的一篇工作,关于生成发型快速切换的一个模型(传统需要6秒,他能压缩到1秒以内),这个跟亦童最近的工作很像,我也很感兴趣,并给他分享了这篇工作,这篇的问题是他们宣称是提升模型推理速度,原因是简化了baseline模型中一些冗余的结构,但是我觉得这种事情应该从model size或者FLOPS上来探讨,而不是一个实证的事情,而且模型加速这点现在更多可能会在并行上创新,并与他进行讨论。另一篇也是苏州大学的工作,关于多prompt任意长度视频生成的功夫做,这很新颖而且很难,他们展现出来的效果一般,而且在提问环节卡壳,这与亦童做的事情很像,还有一篇关于是image dehazing的工作是代讲,感觉没有什么明显的创新之处。这个关于视频生成的那个效果其实不好,明显是不连续的,感觉跨太大了,之前CVPR2024的best paper不过是做数十帧数的生成,此中还融合了物理原理的方法,现在做视频生成的事情就直接硬生成,纯靠算力,依靠对若干节点上的prompt来生成单独的关键帧,然后做插值平滑,这太粗暴了实话说,世界真的是很平滑的吗?事实效果也确实不好,视觉上也明显是不够顺滑的。
晚上第三场我去东校区的Foyer听了关于可解释性AI的workshop,但其实大对数是关于机器人的讨论,不过也有一篇工作是关于使用反事实增强来解释人机交互行为的工作,此外目前机器人的可解释问题还是主要依靠action和language对齐的方法来实现的。
9月17日的最后是welcome reception 非常热闹。chair甚至自己带了吉他和鼓乐,与大家一起娱乐,欧洲人确实是有不同之处的,这种氛围在国内是很少见的。仓廪实而知礼节,衣食足而知荣辱,
- basics
- dnn 的标准组件,稳定和加速训练过程
batch_size, seq_len, hidden_size = 2, 3, 4
- Batch Norm: reduce cross batch size
- mini-batch dimension
- 一般用于图像,不涉及到padding的问题;
- Layer Norm: reduce cross hidden dim
- 一般用于序列,一个 batch size 内存在 padding;
- across the feature dimension.
- RMSNorm: 对 LN 的一种变体,llama
- residual connection + norm
- https://spaces.ac.cn/archives/9009
- Pre LN:
llama
- Post LN:
attention is all you need
- 理解高维 tensor 的 shape (axis/dim)处理
- 最终的输出都不改变 shape
- 理解这三个 norm 的计算过程
import torch
from torch import nn
import transformers
from transformers import AutoModelForCausalLM
from IPython.display import Image
import os
os.environ['http_proxy'] = 'http://127.0.0.1:7890'
os.environ['https_proxy'] = 'http://127.0.0.1:7890'
torch.manual_seed(42)
# 3d tensor
batch_size, seq_len, hidden_size = 2, 3, 4
x = torch.randn(batch_size, seq_len, hidden_size)
先看BN:
x: (a, b, c)
- mean/var 都是 reduce 的过程;
- mean(dim=a) => 返回的 shape 是 (b, c)
- mean(dim=a, keepdim=True) => (1, b, c): 方便 broadcast
- einsum:
ijk->jk
- mean(dim=(a, b)) => 返回的 shape 是 ©
- mean(dim=(a, b), keepdim=True) => (1, 1, c): 方便 broadcast
- einsum:
ijk->k
bn = nn.BatchNorm1d(hidden_size)
bn(x)
这里随机的x
比如长这样:
tensor([[[ 1.9269, 1.4873, 0.9007, -2.1055],
[ 0.6784, -1.2345, -0.0431, -1.6047],
[ 0.3559, -0.6866, -0.4934, 0.2415]],
[[-1.1109, 0.0915, -2.3169, -0.2168],
[-0.3097, -0.3957, 0.8034, -0.6216],
[-0.5920, -0.0631, -0.8286, 0.3309]]])
这会报错:running_mean should contain 3 elements not 4
应该这样写:
bn(x.transpose(1, 2)).transpose(1, 2)
"""
tensor([[[ 1.7943, 1.9214, 1.1306, -1.5846],
[ 0.5278, -1.3052, 0.2633, -1.0345],
[ 0.2006, -0.6557, -0.1505, 0.9930]],
[[-1.2873, 0.2668, -1.8263, 0.4897],
[-0.4746, -0.3108, 1.0412, 0.0451],
[-0.7609, 0.0835, -0.4585, 1.0912]]], grad_fn=<TransposeBackward0>)
"""
所谓tranpose(1, 2)就是简单转置第二和第三维
# 2*3*4 => 2*4*3
x.transpose(1, 2)
mean = x.transpose(1, 2).mean(dim=(0, 2), keepdim=True)
mean
"""
tensor([[[ 0.1581],
[-0.1335],
[-0.3296],
[-0.6627]]])
"""
# 序列的每个位置算均值
torch.einsum('ijk->k', x) / (x.shape[0] * x.shape[1]) # tensor([ 0.1581, -0.1335, -0.3296, -0.6627])
# x.transpose(1, 2).mean(dim=0).mean(dim=1)
x.transpose(1, 2).mean(dim=0, keepdim=True).mean(dim=2, keepdim=True)
"""
tensor([[[ 0.1581],
[-0.1335],
[-0.3296],
[-0.6627]]])
"""
# 2*4*3 => 4*6
x.transpose(1, 2).transpose(0, 1).reshape(4, -1)
"""
tensor([[ 1.9269, 0.6784, 0.3559, -1.1109, -0.3097, -0.5920],
[ 1.4873, -1.2345, -0.6866, 0.0915, -0.3957, -0.0631],
[ 0.9007, -0.0431, -0.4934, -2.3169, 0.8034, -0.8286],
[-2.1055, -1.6047, 0.2415, -0.2168, -0.6216, 0.3309]])
"""
x.transpose(1, 2).transpose(0, 1).reshape(4, -1).mean(dim=-1) # tensor([ 0.1581, -0.1335, -0.3296, -0.6627])
var = x.transpose(1, 2).var(dim=(0, 2), keepdim=True, unbiased=False)
var
"""
tensor([[[0.9717],
[0.7116],
[1.1841],
[0.8291]]])
"""
((x.transpose(1, 2) - mean) / torch.sqrt(var + bn.eps)).transpose(1, 2)
"""
tensor([[[ 1.7943, 1.9214, 1.1306, -1.5846],
[ 0.5278, -1.3052, 0.2633, -1.0345],
[ 0.2006, -0.6557, -0.1505, 0.9930]],
[[-1.2873, 0.2668, -1.8263, 0.4897],
[-0.4746, -0.3108, 1.0412, 0.0451],
[-0.7609, 0.0835, -0.4585, 1.0912]]])
"""
(x - x.mean(dim=(0, 1), keepdim=True)) / torch.sqrt(x.var(dim=(0, 1), unbiased=False, keepdim=True) + bn.eps)
"""
tensor([[[ 1.7943, 1.9214, 1.1306, -1.5846],
[ 0.5278, -1.3052, 0.2633, -1.0345],
[ 0.2006, -0.6557, -0.1505, 0.9930]],
[[-1.2873, 0.2668, -1.8263, 0.4897],
[-0.4746, -0.3108, 1.0412, 0.0451],
[-0.7609, 0.0835, -0.4585, 1.0912]]])
"""
再看LN:
-
element-wise operation
y = x − E [ x ] V a r [ x ] + ϵ ∗ γ + β y = \frac{x - \mathbb{E}[x]}{\sqrt{\mathrm{Var}[x] + \epsilon}} * \gamma + \beta y=Var[x]+ϵx−E[x]∗γ+β
-
关于 V a r [ x ] \mathrm{Var}[x] Var[x]
V a r [ x ] = E [ ( x − E [ x ] ) 2 ] \mathrm{Var}[x] = \mathbb{E}[(x - \mathbb{E}[x])^2] Var[x]=E[(x−E[x])2]
ln = nn.LayerNorm(hidden_size)
ln_out = ln(x)
ln_out
"""
tensor([[[ 0.8716, 0.5928, 0.2209, -1.6853],
[ 1.3440, -0.7473, 0.5552, -1.1519],
[ 1.1111, -1.1985, -0.7703, 0.8577]],
[[-0.2380, 1.0472, -1.5270, 0.7177],
[-0.3243, -0.4803, 1.6947, -0.8900],
[-0.6717, 0.4977, -1.1947, 1.3688]]],
grad_fn=<NativeLayerNormBackward0>)
"""
ln # LayerNorm((4,), eps=1e-05, elementwise_affine=True)
x[0, 0, :] # tensor([ 1.9269, 1.4873, 0.9007, -2.1055])
(x[0, 0, :] - torch.mean(x[0, 0, :])) / torch.sqrt(torch.var(x[0, 0, :], unbiased=False) + ln.eps) # tensor([ 0.8716, 0.5928, 0.2209, -1.6853])
(x - x.mean(dim=-1, keepdim=True)) / torch.sqrt(x.var(dim=-1, unbiased=False, keepdim=True) + ln.eps)
(x - x.mean(dim=-1, keepdim=True))
x - torch.einsum('ijk->ij', x).unsqueeze(2) / x.shape[-1]
总之现在BN用的少,LN用的多
最后看一个RMSNorm
y = x 1 n ∑ i x i 2 + ϵ ∗ γ y = \frac{x}{\sqrt{\frac1n\sum_ix_i^2+\epsilon}} * \gamma y=n1∑ixi2+ϵx∗γ
- https://github.com/meta-llama/llama3/blob/main/llama/model.py#L35C7-L46
- 存在 learnable parameter
class RMSNorm(torch.nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def _norm(self, x):
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
def forward(self, x):
output = self._norm(x.float()).type_as(x)
return output * self.weight
rms = RMSNorm(dim=4)
rms(x)
x / torch.sqrt(x.pow(2).mean(dim=-1, keepdim=True) + 1e-6)
在llama3中使用的正是rmsnorm:
llama3_id = "meta-llama/Meta-Llama-3-8B"
llama3 = AutoModelForCausalLM.from_pretrained(llama3_id, torch_dtype=torch.bfloat16, device_map='auto')
llama3 # 查看模型层
9月18日的keynote speech来自德国Bremen大学的女教授Tanja,我认为是非常精彩的报告,关于基于生物信号建模适应性的认知系统。生物信号(如脑电波)作为一种序列式的信息,确实可以用来做很多事情。Tanja解释了为什么选择生物信号,而非其他的特征,主要还是前人在此的积累已经比较多,这是stand on the giant的。比如生物信号可以用于进行情感检测,包括人类以及其他动物,这是区别于一些基于脸部特征的情感分析的方式,另一个是silent speech,即不出声就能解析文字,挑战在于缺乏音频反馈,我想这可能是针对的聋哑人,他们基于此提出了一种brain-to-text的任务,直接匆匆脑电波中读取文字,这就像是读心术,很fantastic,甚至可以基于脑电波合成声音(sEEG模型),不过Tanja展示了一些合成的声音,效果似乎不是那么好,主要是生成的声音语速非常慢,后面他们做了一些改进,可以更快,但显得并不那么智能,不过他们已经推出了EMG系统,用于为聋哑人合成声音。
9月18日的日程简短,只有两场汇报,第一场我去Library Room听阅,不是因为我感兴趣那边关于神经机器人的汇报,而是因为明天的汇报我也要在Library Room进行,我想提前看一下场地以及适应一下设备。实际上很难听懂,一个是因为研究范围与之交集甚少,另一个是因为6篇report都是歪果仁的汇报,语速太快不是很能跟得上,他们大多数的工作都是基于gym库展开的,6篇无例外地都是关于机械臂,其中Michal Vavrečka做了两篇汇报,他讲得很风趣,很调动气氛,可惜我没太听懂,第一篇大概是关于目标检测的,创新点在于检测的不止是目标,包括action/motion,当然他的目标空间还是很小的,大概只有8种动作,然后他们生成了一些fake motion来作为混淆,他的另一篇改进了PPO算法,提出了一种MultiPPO算法,声称可以广泛用于强化学习,其实感觉是比较小的创新。其他有一篇呢倒是和解释性比较相关,是Miroslav Cibula的工作,他听起来是很纯正的英伦腔,不过讲得确实不怎么好,磕磕绊绊的,他主要是对强化学习(s_t, a_t)到s_{t+1}的一个输出,基于SHAP(就是eraser-base的特征赋分算法)做了一个特征筛选,因为对于机械臂来说,state是连续的,也就是无穷多的,action则可以定义为离散的(比如抓取,可以分为半抓,刚好抓住,紧紧抓住),这样的话整体的state(即s_t和a_t的二元组)就非常多了(即便是采样离散值),他希望基于SHAP来进行特征降维,当然他的题目是Learning Low-Level Causal Relations using a Simulated Robotic Arm,似乎跟因果也能挂上点钩吧。session chair Kristína Malinovská自己也有一偏汇报,是关于mirror neuron system的,她讲得很富有感染力,6篇汇报每篇结束都有至少3个人分别提出问题并讨论,以至于最后这场session都被迫超时了。不得不说,欧洲人在进行学术汇报时,的确是富有热情的,反观我们自己,很多session都是提前结束,大家只是在被动地完成任务,对比确实是鲜明的,我们的确要承认确实做得不够好。
9月18日下午我回到了主会场Aula Magna听阅关于CV中Security and Adversarial Attacks的问题,这其实与我的工作是相关的,不过我听到的三篇汇报都是基于deepfake数据集展开的,这个数据集里面大多数是一些AI换脸之类的假照片,需要训练模型进行识别图像是否被伪造,这本质就是一个图像分类问题,其实与我所希望听到的关于如何attack,如何defend的东西相去甚远,不过就便罢了,5篇paper都来自国内,这场很快就提前半小时结束了。
最后,傍晚,伯尔尼大学生物研究院的Walter Senn进行了一个完全听不懂的keynote speech,我想这应该是关于脉冲神经网络的(Spiking NN),因为明天早上在Library Room的Special Session就是关于这个,这是一个生物学研究的方向,关于如何建模神经突触的信号传播。
后来六点多我出去跑了会儿山,其实早上因为出来的早,而九点才开始keynote speech,所以我先去把上山的路探了一下,所以晚上跑得比较顺畅,上坡确实很难,六分配也不能坚持很久,下坡很陡,看到有人骑车一路而下,看起来特别危险,445左右的速度我都感觉无法控制住身体,其实下坡很伤膝盖,并不能老是这样跑。最后455均配跑了11公里多,累计爬升估计在200米左右,中间相当于翻了一座100多米的山,心率很好,只有143bpm,真的很舒适,不算很累。
昨天是沿着卢加诺湖畔跑的,这样就是山水都跑过了,明天就不一定再跑多少了。后天可能就要提前润了。
import os
from multiprocessing import Process
-
伪多线程 ??
- 因为全局解释器锁(GIL:Global Interpreter Lock)的存在
-
有了多线程之后,阅读分析代码就不再是串行阻塞的了;
fork v.s. spawn
-
在操作系统层面,创建新进程主要有两种方式:
fork()
和spawn()
。- fork():这是UNIX及其派生系统(如Linux)中常用的创建进程的方法。当调用fork()时,当前进程(父进程)被复制(包括代码、数据、堆栈等),产生一个新的进程(子进程)。子进程获得与父进程几乎完全相同的状态,但有自己的独立地址空间。fork()之后通常会跟随一个exec()调用,来运行不同的程序代码。
- spawn():在这种方式下,创建一个新进程并直接指定程序及其参数来运行,而不是复制当前进程的状态。spawn()在Windows系统中是创建新进程的方式。
-
fork
def child_process():
print("这是子进程,其PID为:", os.getpid())
def parent_process():
print("这是父进程,其PID为:", os.getpid())
child_pid = os.fork()
if child_pid == 0:
# 在子进程中
child_process()
else:
# 在父进程中
print("父进程中的子进程PID为:", child_pid)
if __name__ == '__main__':
parent_process()
这是父进程,其PID为: 83031
父进程中的子进程PID为: 83032
这是子进程,其PID为: 83032
- spawn
def child_process():
print("这是子进程,其PID为:", os.getpid())
if __name__ == "__main__":
print("这是父进程,其PID为:", os.getpid())
p = Process(target=child_process)
p.start() # 使用spawn方法启动子进程
p.join() # 等待子进程结束
这是父进程,其PID为: 83287
这是子进程,其PID为: 83289
看一个实例:
import threading
import time
done = False
def work():
counter = 0
while not done:
time.sleep(1)
counter += 1
print(counter)
work()
threading.Thread(target=work, ).start()
input('Press enter to quit')
done = True
带参数,多个线程
def work(text):
counter = 0
while True:
time.sleep(1)
counter += 1
print(f'{text}: {counter}')
threading.Thread(target=work, args=('ABC', )).start()
threading.Thread(target=work, args=('XYZ', )).start()
# threading.Thread(target=work, daemon=True, args=('ABC', )).start()
# threading.Thread(target=work, daemon=True, args=('XYZ', )).start()
t = threading.Thread(target=work, args=('test', ))
t.start()
print('active_count: ', threading.active_count())
threading.active_count()
9月19日,早上的keynote speech是来自洛桑科技学院蓝色大脑的(Blue Brain),Michael W. Reimann,也是Spiking NN方向的,他讲得实在是太快了,我只能听懂很少,我觉得这种研究人脑神经网络的事情很炫酷,不过跨度太大,我们确实对生物神经学所知甚少,他能够解释神经突触如何对不同的语言进行转换,比如中国人是如何听懂英文,这样从神经元的层面来解释翻译的事情。然后主会场Aula Magna的第一场Session是关于segmentation的,因为前面几个基本上都是做的比较传统的语义分割问题,我很感兴趣的是来自复旦大学顾晓东教授进行的分享(他讲得很舒缓,而且很清晰),关于Audio-Visual的语义分割问题,这个非常有趣的,它需要构建模型,在连续的视频帧中,找到可能发声的物品,这个就很有意思,他不只是语义分割,还要做发声物体的预测。这里的重点其实就是如何构建一个有效的Visual-Audio的联合编码器,模型输出就是每帧图片上的一块mask,用于标注那个正在发声的实体,我们对他做了一些提问,比如如果图象中同时存在多个发声的物体将会如何,这确实是复杂的场景,他们还没有考虑这种问题,因为声音目前还是单声道形式的。我则比较关心他们是如何联合编码声音和图像的语义的,事实上他们还是分下来编码,然后通过设计注意力机制(依然是传统的多头注意力)来联系声音与图像信息。
最后就是下午第二场,在Library Room关于Accuracy, Stability, and Robustness in Deep Neural Networks的Special Session。这场Special Session旨在召集研究人员讨论与开发稳定、准确和稳健的数据驱动人工智能相关的最新和未解决的挑战。特别强调了与对抗性扰动的鲁棒性、测试集的稳定性和准确性、从高维设置中的少数示例中学习以及网络架构选择(其层数和计算单元类型)对网络性能准确性和模型复杂性的影响相关的基本问题。会议为研究人员提供了一个开放的论坛,让他们会面并展示最近的贡献,重点关注理论和实际/应用挑战以及提出新的可经验验证的启发式方法的贡献。会议以伦敦国王学院的Ivan Tyukin教授的受邀演讲“构建稳定、准确和稳健的数据驱动人工智能的挑战”开始。随后是从9份提交的论文中精心挑选出来的5篇演讲。演讲涉及的主题非常广泛,从深度ReLU网络准确性的数学分析到视觉图像稳健性的各个方面、阅读理解、后门攻击和不可学习的图像检测。
开场的Keynote speech非常的理论,Tyukin教授围绕精确性和稳定性系统地给我们介绍了七个定理,主旨就是精确性和稳定性本身就是一个tradeoff,想要同时取得两者是很难的。其实关于这件事情我一直想说,就是这个世界到底是稠密的,还是稀疏的?或者更精确地说,到底稠密更好,还是稀疏更好?实际上世界源自混沌,我们现在总是在给模型挑刺,试图在稀疏常见样本间构造更多的“沙子”,让模型感觉刺耳。这是合理的吗?其实真的很难讲,一方面,较于真正稠密的世界,我们现实世界毫无疑问是稀疏的(世界始于混沌,但会是熵减的,就像教授举的例子,一幅有意义的图像的范数是低的,如果给他做干扰,它的范数就会变得很高),但就现实世界而言,我们观测到的东西也是稀疏的。
然后是Session Chair(Vera)自己进行的汇报,Some Comparisons of Linear and Deep ReLU Network Approximation,这是关于很基础的多层感知机(以ReLU作为激活)与线性复合网络的对比,我觉得欧洲人很多时候就喜欢做这些基础性研究,虽然感觉并不太能应用,但是基础研究往往会引出许多很好的理论,这是我们缺少的。
第三个是德国学者Teichmann关于图像分类的鲁棒学习,Robustness of Biologically Grounded Neural Networks against Image Perturbations,然后是我的MADE,尽管准备了讲稿并多次排练修改,但还是超时,导致我有些狼狈,Ivan Tyukin很友善地给我提了一个问题,关于是否在微调中使用LoRA等方法,这是之后可以改进的地方。第五个是南洋理工大学的俞一进行的Unlearnable Examples Detection via Iterative Filtering,最后是浙江大学荣大中进行的Clean-image Backdoor Attacks,这是浙大与阿里联合的一篇工作,主要的贡献就是提出了一种图像分类的有效攻击手段,就是不是微扰图像,而是在标签上下毒,即通过在训练集中取一部分样本,将他们的标签修改为有毒的标签,然后交给模型训练,这样就得到了一个有毒的模型,这样只需要对干净的图片做细微的修改(他们会要求微扰后的图像与原图之差的二范数在阈值λ以内,其实就是这个有毒的模型事实上并没有很好的对样本空间进行划分),就能显著的改变他们的分类预测结果。
作为一个总结,目前的AI热点毫无疑问还是在CV上,这是无可争议的,绝大多数有趣的研究都是基于CV模型的延申,纯NLP很难有前途,而CV中的鲁棒问题也是一个热点,是值得去深究的。
正文
好了,现在我收拾完了东西,可以花点时间来把事情说一说了,其实这几天吃得很好,否则我也不会这么有力气每天结束还能跑一会儿。不得不说注册费还是没白交,每天稳定两次coffee break和lunch break,虽然菜不是很好吃,让中国人头皮发麻的猕猴桃炒饭,以及冰凉的红豆粥,但点心是真的管饱,而且真的很好吃,尤其是那个谷物拼盘,里面是一些麦片,巧克力,葡萄干以及各种坚果,舀一杯再浇上酸奶,是真的太香了,怎么吃都不够。
我并不想写得太多,虽然也拍了许多照片,结识了一些朋友。在昨天跑山的时候,我大概一共录了有15分钟左右的录像,我想我已经留下了许多来过的记录。怎么说呢,这个地方真的很好,但是它并不属于我们。
Silvia送了我一盒巧克力作为饯行礼,我似乎没有什么可以送给她的,就用意文给她写了一封明信片。事实上瑞士的中北部都是德语区,西南角是法语区,我在的东南角一带都是意语区,卢加诺其实只有小部分人能听懂英语,至少Vegan可以,但是Silvia是真的不行,导致我就很难跟她沟通,必须使用谷歌翻译,沟通成本太大,因此每天早上我都会把今天一日的行程翻译成意大利语发送给她,傍晚回来前也要提前通知她我晚上的活动,以防止造成不必要的麻烦。
我觉得自己的文笔很难在短时间内描述得好这几日海量的信息,但是这本身也是见仁见智的。总之我们确实缺少了一些东西,也许这也是与生俱来的一种差异吧。其实能定居在这里的人大多身世不一般,就像Vegan,她的外婆其实是上海人,外公呢,是当年法租界的住客,但是我始终认为,应当尊重不同的生命与生活,其实并没有什么绝对的善恶好坏之分,还是应该多听多看,也便足够了。
搁笔,上海见。
后记
总而言之,这次往返都很顺利,飞机没有晚点,也成功躲开上海的两轮台风侵袭(贝碧嘉和普拉提)。一路上没出错,按计划进行,东西也没丢,目的也基本达成,说句完美不过分。
身体素质得到了验证,卢加诺每天大约5~15℃的气温,我只带了一件长袖外套,其余都是短袖短裤,这都没感冒,而且胃口很好,吃得多,还不用上厕所(反正曼谷到苏黎世以及回程,都是连坐11个小时没站起来过,而且都把两顿机餐都吃完了)。只是人的精力确实有限,返程飞机上没睡着,以至于在曼谷转机时极其痛苦,差不多有连续30个小时没合眼,真的快困死了,但是又不方便休息,就一直折磨自己。等上了回上海的飞机后,一头攒死在座位上。
事实上,手表显示,今天的身体电脑最高只有37,而我晚上七点到学校后,还是先去跑了1500米,然后久违地去蜀地源放纵了一下(其实自从新食堂修好后,我已经三个星期没去蜀地源放纵了,这么说,欧洲的饮食确实油放得少,而且基本都是冷食生食为主,这确实是比较健康的一种吃法,我们油盐还是吃太多),吃不饱,完全吃不饱,怎么都吃不饱。最后十点回宿舍后,身体电量已经只剩3,我想自己应该是快要死了,从我离开silvia家后,一共31个小时才到宿舍,中间没有上过大号,一共吃了三顿机餐,以及8个小蛋糕,最重要的是,基本上没怎么睡着,真的快死了。
不过只要等两天能缓过来,我应该立刻能恢复到非常好的状态。事实上这一周得到了充分的休息(睡眠真的很好,床很舒服,每天都是六点半左右自然醒,相当的舒服),但也没有缺少锻炼。今年吴征把高百队长让给了我,也算是圆了我的心愿,我真心想带一支能打的队伍,去冲一冲10月26日的上海站前六。另外我也很高兴宋某的回归,他将成为重要助力。
别的我实在不想废话了,我想要赶紧休息。希望明早醒来时,虞山的某位还没下撤吧(35K组有个跟我同名同姓的人,有趣)。