《白马非马》
月晕梦觉三更外,辗侧不寐天欲白。
长街小巷人不在,闭户方晓余空斋。
淫行庸碌民风改,凛冬难去花未拆。
粉饰贪墨莫仇忾,时有灾异毋自哀。
——囚生
辗转多时我还是回到了这里继续写,希望这篇可以写得更久一些,各种意义上。
本来是决定死磕到底,因为很想亲眼看看上位者何时才愿意在这荒唐的局面下做出让步。可是忽然又觉得拿别人的过错来惩罚自己实非明智之举,尤其是伙食质量日益参差不齐,越发无法满足日常训练需求。如果大多数人都坚定地选择留下来,必然是会对决策产生冲击的,可惜事与愿违。
走得很匆忙,提前一天半买票,然后赶忙办好各项事宜。虽然很不愿意这么形容,但是更像是一场大逃亡。出站后就被贴上各种标签集中管控,在被站务人员吆喝着快点走的时候,俘虏即将进入集中营的既视感跃然心头,前面的老爷子死活不肯加快步伐,透过背影能看出骨子里的那股倔强。
好在走出封楼警戒线后如释重负,两个月的高压状态得到缓解。还认识一个同行回无锡的学妹,结伴走了一遭,有种跟活人打交道的新鲜感。也算不得太坏罢。
最后是一些罔论:
- 我一直认为要多了解少发声,即悉实而慎论,因为有一百个人赞同你,就会有一千个人中伤你,从cdb劳神费力写了一个月《昨日谈》后选择弃坑可见一斑。但是每个人都不发声,这就是一件好事吗?也许某些场合下,我们只是被禁锢了声带,使得本该属于强弱群体之间的矛盾莫名其妙地转变成弱势群体内部的矛盾。
- 我一直以来还有一个坚定的观点不争对错,因为历史无法重演,无法证明也无法证伪若干决策事实上的合理性,甚至连合理性本身就缺少客观的评价方式,因此我总是会固执己见地走自己的歪门邪道。但是在这次的事件上我是真的动摇了,或许80%的人认为正确还不足以证明正确、99%还不能、那么99.99%呢?其实都没有区别,只要剩下的0.01%让99.99%发不出声,那0.01%就是100%。
声明:本文的观点仅代表个人看法,不针对任何对象,请勿妄加揣度。本文将以备忘录形式展开更新直至字数到达文章可发布的上限值,目的是督促自己每天能学到些有用的新东西,与诸君共勉。
文章目录
- 序言
- 2022-05-25
2022-05-25
两个零碎的知识点:
-
关于Python类中私有数据类型的自动改名问题(双下划线前缀),详见官方文档解释https://docs.python.org/zh-cn/3/tutorial/classes.html#private-variables
-
关于数组拼接的易忘易混点(以torch.tensor类型为例,numpy.ndarray以及tensorflow类似),即区分torch.cat,torch.stack,torch.vstack,torch.hstack四个函数:
-
一维情况:
假定有2个形状为torch.Size([3])的数组a1和a2,则有如下结论:
① torch.cat([a1, a2], axis=0)的形状为torch.Size([6]),即是左右拼接,且axis参数只能取0,不能取别的值。
② torch.stack([a1, a2])的形状为torch.Size([2, 3]),即是上下拼接;
③ torch.hstack([a1, a2]])的形状为torch.Size([6]),等价于torch.cat([a1, a2], axis=0);
④ torch.vstack([a1, a2]])的形状为torch.Size([2, 3]),等价于torch.stack([a1, a2]); -
二维情况:
假定有2个形状为torch.Size([3, 4])的数组a1和a2,则有如下结论:
① torch.cat([a1, a2], axis=0)的形状为torch.Size([6, 4]),直观上看是上下拼接;torch.cat([a1, a2], axis=1)的形状为torch.Size([3, 8]),直观上看是左右拼接;总结axis取值决定结果的形状是在第几维上进行叠加;
② torch.stack([a1, a2])的形状为torch.Size([2, 3, 4]),即是上下堆叠;
③ torch.hstack([a1, a2]])的形状为torch.Size([3, 8]),等价于torch.cat([a1, a2], axis=1);
④ torch.vstack([a1, a2]])的形状为torch.Size([6, 4]),等价于torch.cat([a1, a2], axis=0); -
三维情况:
假定有2个形状为torch.Size([3, 4, 5])的数组a1和a2,则有如下结论:
① torch.cat([a1, a2])的规律与二维情况相同,即取决于axis的取值将会得到torch.Size(6, 4, 5),torch.Size(3, 8, 5),torch.Size(3, 4, 10)三种不同的结果;
② torch.stack([a1, a2])的形状为torch.Size([2, 3, 4, 5]),仍是上下堆叠;
③ torch.hstack([a1, a2]])的形状为torch.Size([3, 8, 5]),等价于torch.cat([a1, a2], axis=1);
④ torch.vstack([a1, a2]])的形状为torch.Size([6, 4, 5]),等价于torch.cat([a1, a2], axis=0); -
更高维情况的规律总结:torch.cat最好理解,根据axis的取值决定最终形状(除axis所在维外,其他维各个数组的形状必须相同),torch.hstack与torch.vstack总是分别对应torch.cat取值axis=1与axis=0的情况,torch.stack总是会使得结果的维数+1,即简单的将所有数组拼凑起来,但是必须要求所有数组的形状都相同。
另外numpy的情况完全相同,其中torch.cat替换为numpy.concatenate
-
-
关于数组形状重构的易忘易混点(以torch.tensor类型为例),即区分torch.reshape,torch.view,torch.transpose,torch.permute
import torch as th t1 = th.FloatTensor([ [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], ], [ [-1, -2, -3, -4], [-5, -6, -7, -8], [-9, -10, -11, -12], ] ]) print(t1) print(t1.view(3, 2, 4)) print(t1.reshape(3, 2, 4)) print(t1.permute(1, 0, 2))
输出结果为:
tensor([[[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 9., 10., 11., 12.]], [[ -1., -2., -3., -4.], [ -5., -6., -7., -8.], [ -9., -10., -11., -12.]]]) tensor([[[ 1., 2., 3., 4.], [ 5., 6., 7., 8.]], [[ 9., 10., 11., 12.], [ -1., -2., -3., -4.]], [[ -5., -6., -7., -8.], [ -9., -10., -11., -12.]]]) tensor([[[ 1., 2., 3., 4.], [ 5., 6., 7., 8.]], [[ 9., 10., 11., 12.], [ -1., -2., -3., -4.]], [[ -5., -6., -7., -8.], [ -9., -10., -11., -12.]]]) tensor([[[ 1., 2., 3., 4.], [ -1., -2., -3., -4.]], [[ 5., 6., 7., 8.], [ -5., -6., -7., -8.]], [[ 9., 10., 11., 12.], [ -9., -10., -11., -12.]]])规律总结:
① .permute实现的是维数交换,一般是可以得到所期望的结果,以.permute(1, 0, 2)为例,原数组在[i, j, k]位置的元素,在新数组中转移到[j, i, k]。如在机器学习常用于多组同类型的数据通过torch.stack堆叠后,将torch.stack多出来的那一维调整到其他位置(比如为了将batchsize维调整到最前面);
② torch.transpose每次只能置换两个维度的次序,PyTorch中文文档中的翻译有误导性,容易误解为只能对二维矩阵进行转置;以tensor.permute((1, 2, 0))为例,其等价于tensor.transpose(0, 2).transpose(0, 1)
③ .reshape与.view方法得到的结果是完全相同的,本质就是先创建一个形状为参数值的空数组,然后按照原数组的维数顺序依次将所有数值填入空数组,因此很可能会得到一个数据完全被打乱的结果;
④ 特别注意的一个点是.view方法是直接在原张量所在的储存地址上直接进行转换,.reshape则是重新开辟新的内存空间,因此前者更快也更节约资源。但是.view仅针对连续存储的张量(如直接使用torch.FloatTensor开辟的张量),而类似torch.stack拼接的张量得到的是不连续存储的,则无法使用.view方法进行转换。其他无法使用.view方法进行转换的方法目前尚不明晰。



