【连载】囚生CYの备忘录
时间:2022-10-19 07:00:00
序言
《白马非马》
月晕梦三更外,辗侧不眠天欲白。
长街小巷人不在,闭户方晓余空斋。
淫行平庸民风改,冬难去花未拆。
粉饰贪墨莫仇,有灾难不自悲。
——囚生
辗转时候,我还是回到这里继续写,希望这篇文章能写得更久,各种意义上。
最初,我决定坚持到底,因为我想看看上级什么时候愿意在这种荒谬的情况下做出让步。然而,我突然觉得用别人的错误来惩罚自己是不明智的,尤其是食品质量越来越不均衡,越来越不能满足日常培训的需要。如果大多数人坚定地选择留下来,他们将不可避免地对决策产生影响,但事与愿违。
匆匆忙忙,提前一天半买票,然后赶紧做好各种事情。虽然不愿意这样描述,但更像是一场比赛大逃亡。出站后,他被贴上各种标签进行集中控制。当车站工作人员喊着要快点离开时,即将进入集中营的囚犯们的视觉感受跳到了他们的心中。他们面前的老人拒绝加快速度。通过他们的背影,我们可以看到他们骨子里的固执。
幸运的是,走出封闭的建筑警戒线后,他松了一口气,两个月的高压状态得到了缓解。我还认识一个同学回到无锡,一起散步,有一种与活人打交道的新鲜感。这并不太糟糕。
最后是一些罔论:
- 我一直以为要多了解少发声,即悉实而慎论,因为有一百个人同意你,一千个人会伤害你cdb劳神辛辛苦苦写了一个月《昨日谈》,选择弃坑可见一斑。但是每个人都不说话,这是好事吗?也许在某些情况下,我们只是被禁锢在声带里,使本应属于强弱群体的矛盾莫名其妙地转化为弱势群体内部的矛盾。
- 我一直有坚定的观点不争对错,事实上,由于历史不能重演,无法证明或证伪若干决策合理性,甚至连合理性缺乏客观的评价方法,所以我总是固执地走自己的路歪门邪道。但我真的在这次事件中动摇了。也许80%的人认为正确不足以证明正确,99%不能,所以99.99%呢?没有区别,只要剩下的0.01%让99.99%不出声,0.01%就是100%。
声明:这篇文章的观点只代表个人观点,而不是针对任何对象。不要猜测。本文将以备忘录的形式更新,直到字数达到文章可以发表的上限,以督促自己每天学习一些有用的新东西,鼓励你。
文章目录
- 序言
-
- 2022-05-25
- 2022-05-26
- 2022-05-27
- 2022-05-28
- 2022-05-29
- 2022-05-30
- 2022-05-31
- 2022-06-01
- 2022-06-02
- 2022-06-03
- 2022-06-04
- 2022-06-05
- 2022-06-06~2022-06-07
- 2022-06-08
- 2022-06-09~2022-06-11
- 2022-06-12~2022-06-13
- 2022-06-14~2022-06-15
- 2022-06-16~2022-06-17
- 2022-06-18~2022-06-19
- 2022-06-20~2022-06-21
- 2022-06-22
- 2022-06-23~2022-06-24
- 2022-06-25~2022-06-26
- 2022-06-27~2022-06-29
- 2022-06-30~2022-07-02
- 2022-07-03~2022-07-04
- 2022-07-05~2022-07-06
- 2022-07-07
- 2022-07-08~2022-07-10
- 2022-07-11~2022-07-14
- 2022-07-15~2022-07-17
- 2022-07-18~2022-07-19
- 2022-07-20~2022-07-25
2022-05-25
关于数组拼接的易忘易混点(torch.tensor
类型为例,numpy.ndarray
以及tensorflow
类似),即区分torch.cat
,torch.stack
,torch.vstack
,torch.hstack
四个函数:
-
一维情况:
假设有两种形状
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])
; -
二维情况:
假设有两种形状
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)
; -
三维情况:
假设有两种形状
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
2022-05-26
- 目前计划花一周时间把斯坦福的CS224N-winter2022的课程从头到尾过一遍,最好能把课程作业也一起做掉,更一篇长博客,CAIL2021暂时搁置,实话说CAIL2021真的是有认真投入,却迟迟不能有所进展,总不能在一棵树上吊死,换件事情做调整心态。
- 这两个月学得太少,想必已是落后很多,对自己有些失望。好在目前多少还能有点容错率,人一定是要不断学习才能保持状态的,周更博客一定程度还是可以鞭策自己进行高效的知识汲取,太怠惰了cy。
- 29号应该就能走,新通知是5+2+7。佳明FR245后天到货,跟老妈放了狠话今年夏天必将10公里跑进40分钟,谁也别想阻挠我。
关于数组形状重构的易忘易混点(以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.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.],
[ -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
方法进行转换的方法目前尚不明晰。
2022-05-27
- 无事可做,一整天都在狂肝CS224N,午觉都没睡,总归还是学到不少东西,等笔注详实一些后先发出来,后续再接着更新。作业的答案也在我的GitHub仓库同步更新,争取一周之内把这事儿给结掉。
- 佳明FR245已经到货,只是这两天训练时把膝盖给磕破了,还好手头有创可贴应急。昨天老妈送了荔枝和苹果,说起来因为这两个月吃不到水果,身上好多伤口都迟迟不能消退,以前天天吃水果没有感觉,还以为自然愈合是很平常的事情,直到流了半个月鼻血后,才知道有的吃是多么奢侈的事情。
Python程序计时可以使用timeit
中的timer
与timeit
方法,前者为单次计时,后者默认100000次计时取前三快的用时取平均。注意使用到的变量需要在globals
参数中以字典的形式进行声明,比如:
import timeit
import random
def sample(n):
samples = []
for i in range(n):
samples.append(random.choice(list("ABC")))
return samples
n = 10
timeit.timeit('sample(n)', globals={
'random': random, 'sample': sample, 'n': n}),
在Jupyter中可以直接使用魔法命令%timeit
对指定代码运行时间进行计时:
%timeit sample(10)
也可以自定义装饰器来对指定函数进行计时,在指定需要计时的函数定义前添加@timer
装饰,每次运行函数即可输出运行时间:
import time
import random
from functools import wraps
# 程序计时的装饰器
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
returned = func(*args, **kwargs)
end_time = time.time()
print('Function `{}` runtime is {} seconds.'.format(func.__name__, end_time - start_time))
return returned
return wrapper
@timer
def sample(n):
samples = []
for i in range(n):
samples.append(random.choice(list("ABC")))
return samples
sample(10)
若忽略装饰器定义中wrapper
函数的return
,则被装饰的函数的返回值将无法获取:
2022-05-28
- 肝完前四讲,发现CS224W也不错,可以一并肝掉。
- 估计还得多挨一天,不过效率很好,反正回去也是一样关禁闭,不如再白吃白喝两天。
- CSDN现在字数限制太过于苛刻,以前那种长博客根本发不出来,只能分篇发布,感觉这篇可能写不到一个月可能就要封篇,很无语。要不之后就用专栏专门收录这类博客吧,懒得吐槽了。
CS224N学习随想:
- 负采样的核心在于定义损失函数,其目的既能扩充规模,也能平衡样本频率。
- 依存分析以及句法分析目前看来有点类似证明图的生成,很类似基于路径的一些方法,因此可能动态规划与强化学习可能是流行的方向,虽然并没有查阅到最新的研究。
顺手记录一下DataFrame.apply
函数的易忘点:
axis=1
时,apply
中的apply_func
的参数表示数据表一行的所有数据,即对每行的数据进行运算得到一个数值,生成一个新的列。
-
注意可能运算得到若干值(即返回值是一个
tuple
,如果是list
也可以),此时可以通过设置参数return_type='expand'
实现生成多个列,下面是一个例子:df = pandas.DataFrame({ 'array1': [[1, 2, 3], [4, 5, 6], [7, 8, 9]] }) print(df) print(df[['array1']].apply(lambda row: (row[0][0], row[0][1], row[0][2]), axis=1, result_type='expand')) # 输出结果 """ array1 0 [1, 2, 3] 1 [4, 5, 6] 2 [7, 8, 9] 0 1 2 0 1 2 3 1 4 5 6 2 7 8 9 """
axis=0
时,apply
中的apply_func
的参数表示数据表一列的所有数据构成的一个pandas.Series
,若对每列的数据进行运算:
-
返回一个数值,则生成一个
pandas.Series
,比如取每列的最大值,平均数,众数:import pandas from collections import Counter df = pandas.DataFrame({ 'array1': [1, 2, 3, 4], 'array2': [1, 4, 9, 16], 'array3': [1, 8, 27, 64], }) print(df.apply(lambda column: column.max(), axis=0))
-
返回若干数值,此时生成一个
pandas.DataFrame
,字段名不变,行数为返回值的数量,比如计算每列的移动平均:df = pandas.DataFrame({ 'array1': [1, 2, 3, 4], 'array2': [1, 4, 9, 16], 'array3': [1, 8, 27, 64], }) print(df.apply(lambda column: [(column[i] + column[i + 1]) / 2 for i in range(len(column) - 1)], axis=0))
2022-05-29
- 然后莫名其妙地中午就回家了,门磁来得太快就像龙卷风,老老实实关个9天,至少能把CS224N肝完(目前进度6/19,项目开在我的GitHub),认真过一遍加上作业巩固的确还是很有用的,把很多模糊的点都搞清楚。
- 佳明FR245到手,准备猛练,这几天就先用跑步机凑合,今天30分钟7km,感觉应该没有掉得太多,不过跑步机跟路跑差别还是挺大。
- 晚上看到SXY在听《潇洒走一回》,是真喜欢听老歌,追新与怀旧形成了统一,人真是神奇的动物。
如何将预训练好的词嵌入融合到模型中继续训练?
比如embeddings
是一个shape(30000, 50)
的矩阵,那么我们可以直接在神经网络模型中定义:
self.embeddings = nn.Parameter(torch.tensor(embeddings))
然后编写从self.embeddings
中取词向量的方法即可替代正常情况下的用法(即self.embeddings(x)
):
def get_embeddings(self, w):
# @param w (Tensor): input tensor of word indices (batch_size, n_features)
# @return x (Tensor): tensor of embeddings for words represented in w (batch_size, n_features * embed_size)
x = self.embeddings[w]
x = x.view(w.size()[0], -1)
return x
或许也可以用另一个方法,定义self.embeddings = nn.Embedding(30000, 50)
,然后初始化它的权重,即将embeddings
赋值self.embeddings.weight
,暂未验证这种方法的正确性。
2022-05-30
- 在憋大招,准备六一腹泻式发布一大波博客。
- 今天做核酸竟然让我下楼到路边做(目前还在第一个7天,第二个7天还没到)。实话说门磁装了跟没装也差不了多少,又没人来检查,完事还要我自己送回社区,那我直接把感应器撕下来跟信号源贴在一起,后台不就只能看到我的门是一直关着的。有多少人能做到单人单套房还绝对不出门呢?到头来不过是法不责众。
- 所以说理想这种东西只能一个人去追求,一群人没有理想可言,上头做事太理想,下面就只能疲于应付,要么就屈尊下来走走看看,人不是机器,肉食者也做不了程序员。
BLEU指数原理:
p n = ∑ ngram ∈ c min ( max i = 1 , 2 , . . . , n Count r i ( ngram ) , Count c ( ngram ) ) ∑ ngram ∈ c Count c ( ngram ) BP = { 1 if len ( c ) ≥ len ( r ) exp ( 1 − len ( r ) len ( c ) ) otherwise BLEU = BP × exp ( ∑ n = 1 k λ n log p n ) p_n=\frac{\sum_{\text{ngram}\in c}\min\left(\max_{i=1,2,...,n}\text{Count}_{r_i}(\text{ngram}),\text{Count}_{c}(\text{ngram})\right)}{\sum_{\text{ngram}\in c}\text{Count}_c(\text{ngram})}\\ \text{BP}=\left\{\begin{aligned} &1&&\text{if len}(c)\ge\text{len}(r)\\ &\exp\left(1-\frac{\text{len}(r)}{\text{len}(c)}\right)&&\text{otherwise} \end{aligned}\right.\\ \text{BLEU}=\text{BP}\times \exp\left(\sum_{n=1}^k\lambda_n\log p_n\right) pn=∑ngram∈cCountc(ngram)∑ngram∈cmin(maxi=1,2,...,nCountri(ngram),Countc(ngram))BP=⎩
⎨
⎧1exp(1−len(c)len(r))if len(c)≥len(r)otherwiseBLEU=BP×exp(n=1∑kλnlogpn)
其中 c c c是机器翻译得到的序列, r i r_i ri是标准翻译的序列(可能会有多个), len ( r ) \text{len}(r) len(r)是从 r i r_i ri中找一个长度最接近 c c c的序列(如果有多个长度最近的则选那个最短的), k k k是指定的最长的 ngram \text{ngram} ngram,一般取 4 4 4, λ i \lambda_i λi是一系列累和为 1 1 1的权重系数。
顺手照公式写了个BLEU指数的脚本:
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
# 计算BLEU指数
import numpy
from collections import Counter
def calc_bleu(nmt_translation, reference_translations, lambdas, k=4):
# 权重系数的长度应当与ngram的最大长度相同
assert len(lambdas) == k
# 期望输入的是已经分好词的两个语句序列, 否则需要首先进行分词
if isinstance(nmt_translation, str):
nmt_translation = nmt_translation.split()
for i in range(len(reference_translations)):
if isinstance(reference_translations[i], str):
reference_translations[i] = reference_transla