【完结】囚生CYの备忘录(20220906-20221120)

序言

下午田径队第一次训练。其实我有点不太想去,一来太阳特别晒,二来自七月下旬住院回来之后,整个八月只进行了五六次路跑,这学期回来已有一周,每天跑2km就足以折煞我,心理落差特别大。

可是转念又想昨天偶遇王炳杰相谈甚欢,暑期又给陈嘉伟的队伍做指导老师,不去队里露脸见个面有点说不过去,之前也注意到李婷玉也住在三门路13号楼,尽管以我的能力可能也不太能搞得好关系吧,反正难得队里有另一个住在同一栋楼的博士,说不定以后能多一点机会呢… 所以尽管时间卡得很紧,还是去训练。

4×15开合深蹲跳,4×10箭步深蹲跳,4×15俯卧挺身,2×60次的交替抬腿跨栏,最后3000米慢跑恢复,大半年没上过这种强度,标准不标准得都勉强坚持下来,差点就要交代在田径场。想到王炳杰暑期每天还能四五点起来跑步,依然保持着10km以上的体能,我真的是要羞愧得无地自容。

说到底运动是能改变一个人的精神面貌,正如前文收尾写得那样,八月份如同泥石流般的颓败,也不知道自己就突然想摆烂到底,找不到可以刺激自己神经的支撑点。但是这场训练之后,我又找回了去年那种做事的激情。

作为一个开端这是值得乐观的,尽管在未来无穷的变数里我仍未发现那束属于自己的光芒,它或有或无,或是注定错失,然而似乎我已经别无选择,大不了自己成为一束最弱的光去点亮下一代罢。



20220906

关于torch的RNN模块做一个详解记录,重点要注意输入输出张量,以及参数张量的形状,另外像LSTM的输出相比GRU以及RNN要多一个 c i c_i ci,GRU与RNN是相似的,RNN类的两个输出是有关联的,具体见下面代码中的注释:

  • LSTM网络由若干层的LSTM层构成,每个LSTM层中包含若干LSTM的单元(数量一般为序列长度),每一个LSTM层里面有两条线贯穿始终,即 h t , c t h_t,c_t ht,ct,因此需要设置初始的 h 0 h_0 h0 c 0 c_0 c0,默认值应该是零张量,需要自定义的话,形状是(num_layers, batch_size, hidden_size),如果是双向,则是2倍的num_layers,

    下面这个例子里说明的是,hidden_output其实是最后一个LSTM层所有 h t h_t ht的合并输出,final_states则是每一个LSTM层的最后一个单元格的输出,即每一层的(h_n, c_n)。

import torch
from torch.nn import Module, Embedding, Linear, Dropout, LSTM, GRU, Sigmoid, CrossEntropyLoss, functional as F

x = torch.FloatTensor(32, 4, 256)
lstm = LSTM(input_size=256,
			hidden_size=128,
			num_layers=2,
			batch_first=True,
			bidirectional=False)

# initial_states = (torch.autograd.Variable(torch.zeros(self.n_layers + int(self.is_bidirectional) * self.n_layers, batch_size, self.d_output)).to(DEVICE),
                  # torch.autograd.Variable(torch.zeros(self.n_layers + int(self.is_bidirectional) * self.n_layers, batch_size, self.d_output)).to(DEVICE))

hidden_output, final_states = lstm(x)
print(hidden_output.shape)		# (batchsize, seq_len, (1+bidirectional)*hidden_size)
print(final_states[0].shape)	# ((1+bidirectional)*num_layers, batchsize, hidden_size)
print(final_states[1].shape)	# ((1+bidirectional)*num_layers, batchsize, hidden_size)


print(hidden_output[:, -1, :].squeeze() == final_states[0][-1, :, :].squeeze())	# True
print(hidden_output[:, -1, :].squeeze() == final_states[1].squeeze())	# False
  • RNN网络类似,可以指定初始状态 h 0 h_0 h0,但是final_states只有一个值:

    rnn = RNN(input_size=256, hidden_size=128, num_layers=2, batch_first=True, bidirectional=False)
    
    hidden_output, final_states = rnn(x)
    
    print(hidden_output.shape) # [batchsize, seqlen, hidden_size]
    print(final_states.shape)	# [num_layers, batchsize, hiddensize]
    
    • input_size – 输入x的特征数量。
    • hidden_size – 隐层的特征数量。
    • num_layers – RNN的层数。
    • nonlinearity – 指定非线性函数使用tanh还是relu。默认是tanh
    • bias – 如果是False,那么RNN层就不会使用偏置权重 b i h b_ih bih b h h b_hh bhh,默认是True
    • batch_first – 如果True的话,那么输入Tensor的shape应该是[batch_size, time_step, feature],输出也是这样。
    • dropout – 如果值非零,那么除了最后一层外,其它层的输出都会套上一个dropout层。
    • bidirectional – 如果True,将会变成一个双向RNN,默认为False
  • GRU网络:

    gru = GRU(input_size=256, hidden_size=128, num_layers=2, batch_first=True, bidirectional=False)
    
    hidden_output, final_states = gru(x)
    
    print(hidden_output.shape) # [batchsize, seqlen, hidden_size]
    print(final_states.shape)	# [num_layers, batchsize, hiddensize]
    
    

    GRU的输出与RNN完全相同,但是也可以指定初始状态 h 0 h_0 h0,GRU是只有一条线贯穿始终,与LSTM有所区别。

  • LSTMCell,RNNCell,GRUCell

    这个就是每一个LSTM层中包含的若干LSTM的单元的一个,具体而言输入输出形状:

    from torch.nn import LSTMCell, RNNCell, GRUCell
    import torch
    
    x = torch.FloatTensor(32, 256)	# (batchsize, input_size)
    h_0 = torch.FloatTensor(32, 512) # (batchsize, hidden_size)
    rnncell = RNNCell(input_size=256, hidden_size=512, bias=True, nonlinearity='tanh')
    h_1 = rnncell(x, h_0)	# (batchsize, hidden_size)
    
    x = torch.FloatTensor(32, 256)	# (batchsize, input_size)
    h_0 = torch.FloatTensor(32, 512) # (batchsize, hidden_size)
    grucell = GRUCell(input_size=256, hidden_size=512, bias=True)
    h_1 = grucell(x, h_0)	# (batchsize, hidden_size)
    
    x = torch.FloatTensor(32, 256)	# (batchsize, input_size)
    h_0 = torch.FloatTensor(32, 512) # (batchsize, hidden_size)
    c_0 = torch.FloatTensor(32, 512) # (batchsize, hidden_size)
    
    lstmcell = LSTMCell(input_size=256, hidden_size=512, bias=True)
    
    h_1, c_1 = lstmcell(x, (h_0, c_0))	# both (batchsize, hidden_size)
    

20220907

  • 疼,大臂疼、肩膀疼、大腿前侧疼、大腿后侧疼、屁股疼。起不来床,下不了楼,蹲不了坑,但也得硬着头皮一大早去实验室,晚上临走前操场小跑了2圈,这种机械式的routine实在是累死,我决定至少九月份还是要坚持训练的,之后实在精力不够只能慢慢降低跑步占比,真的是被生活逼上绝路。

TreeRNN一个Cell的编码器:

class TreeRNNEncoder(Module):
	"""2022/08/26 11:50:47
	为每个句法树节点建立独立的RNN网络, 输入为句法树节点对应下面的若干分词或从句的表示, 维数为(n_words, input_size), 输出为(output_size)
	:param args: QAModelConfig类型的配置
		   args.tree_rnn_encoder_tag_name			: 节点名称, setting.py中STANFORD_SYNTACTIC_TAG中定义的名称
													  这里区分是否为STANFORD_POS_TAG(叶子节点)
													  叶子节点必然汇入非叶节点, 非叶节点也汇入非叶节点
													  因此叶子节点的输入输出维数可以不同, 非叶节点的输入输出维数必须相同
		   args.tree_rnn_encoder_rnn_type			: 使用的RNN编码器, 可选值为RNN, LSTM, GRU
		   args.tree_rnn_encoder_num_layers			: RNN编码器堆叠的层数
		   args.tree_rnn_encoder_bidirectional		: RNN编码器双向标志
		   args.tree_rnn_encoder_squeeze_strategy	: 压缩RNN输出的strategy, 可选值有mean(取均值), final(只取最后一层输出), fixed_weight(固定权重加权平均), variable_weight(可变权重加权平均, 即作为参数训练)"""
	def __init__(self, args, tag_name):	
		super(TreeRNNEncoder, self).__init__()
		self.args = deepcopy(args)
		self.tag_name = tag_name
		
		# 句法树根节点
		if self.tag_name == 'ROOT':											
			self.input_size = self.args.tree_rnn_encoder_leaf_hidden_size
			
		# 词性标注: 即叶子节点
		elif self.tag_name in STANFORD_POS_TAG:							
			if self.args.document_embedding is not None:
				# 文档嵌入: 禁用, 句法树模型只能使用词嵌入
				raise Exception('Please use word embedding rather than document model !')
			elif self.args.word_embedding is None:
				# 顺序编码值嵌入: 需要建立嵌入层
				self.embedding_layer = Embedding(num_embeddings=pandas.read_csv(REFERENCE_TOKEN2ID_PATH, sep='\t', header=0).shape[0], 
												 embedding_dim=self.args.default_embedding_size)
				self.input_size = self.args.default_embedding_size
			elif self.args.word_embedding == 'word2vec':
				# word2vec词嵌入
				self.embedding_layer = None
				self.input_size = self.args.size_word2vec			
			elif self.args.word_embedding == 'fasttext':
				# fasttext词嵌入
				self.embedding_layer = None
				self.input_size = self.args.size_fasttext
			else:
				raise Exception(f'Unknown word embedding: {self.args.word_embedding}')

		# 句法结构标注: 即非叶节点
		elif self.tag_name in STANFORD_SYNTACTIC_TAG:						
			self.input_size = self.args.tree_rnn_encoder_leaf_hidden_size	
		else: 
			raise Exception(f'Unknown syntactic tag: {tag_name}')	
		
		self.output_size = self.args.tree_rnn_encoder_leaf_hidden_size
		self.sequence_encoder = eval(self.args.tree_rnn_encoder_rnn_type)(input_size	= self.input_size, 
																		  hidden_size	= int(self.output_size / (1 + self.args.tree_rnn_encoder_bidirectional)),	# 双向RNN的输出维数是hidden_size的2倍
																		  num_layers	= self.args.tree_rnn_encoder_num_layers,
																		  batch_first	= True, 
																		  bidirectional	= self.args.tree_rnn_encoder_bidirectional)
													
		# 2022/09/05 22:12:01 这里我想的是可以考虑在variable_weight的情况下引入一个Parameter类型的一维权重张量self.squeeze_weight
		# 2022/09/05 22:12:01 但是需要确保该张量的元素和为1, 这比较麻烦, 或许可以在损失函数中引入self.squeeze_weight的正则项, 但是这太麻烦了
		# 2022/09/05 22:12:01 于是我决定暂时不实现这种情况了
		if self.args.tree_rnn_encoder_squeeze_strategy in ['mean', 'final']:
			self.args.squeeze_weight = None
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'fixed_weight':
			# 2022/09/06 14:21:45 默认的固定权重定义为等比数列1/2, 1/4, 1/8, ...
			self.squeeze_weight = torch.Parameter(torch.FloatTensor([2 ** (-i) for i in range(self.args.default_max_child)]), requires_grad=False)
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'variable_weight':
			# 2022/09/06 14:21:45 即将上面默认的固定权重改为可修改(requires_grad=True)
			# self.squeeze_weight = torch.Parameter(torch.FloatTensor([2 ** (-i) for i in range(1, self.args.default_max_child)]), requires_grad=True)
			raise NotImplementedError
		else:
			raise Exception(f'Unknown squeeze_strategy: {self.squeeze_strategy}')
		
	def forward(self, x):
		"""2022/09/03 15:58:30
		前馈逻辑: 
		:param x: (batch_size, seq_len, input_size), 目前感觉batchsize或许只能取1"""
		# (batch_size, seq_len, input_size) -> (batch_size, seq_len, (1 + bidirectional) * hidden_size)
		y1, _ = self.sequence_encoder(x)		
		
		# (batch_size, seq_len, (1 + bidirectional) * hidden_size) -> (batch_size, (1 + bidirectional) * hidden_size)
		if self.args.tree_rnn_encoder_squeeze_strategy == 'mean':
			y2 = torch.mean(y1, axis=1)			
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'final':
			y2 = y1[:, -1, :]
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'fixed_weight':
			sequence_length = x.shape[1]
			for i in range(1, sequence_length):
				y2 = y1[: -i, :] * self.squeeze_weight[i - 1]
			y2 += y1[:, 0, :] * self.squeeze_weight[sequence_length - 2]# 初始位置的权重跟下一个位置的权重是相同的, 即1/2, 1/4, 1/8, 1/8
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'variable_weight':
			# 2022/09/06 22:41:04 实现逻辑与fixed_weight的情况是一样的
			raise NotImplementedError
		else:
			raise Exception(f'Unknown squeeze_strategy: {self.squeeze_strategy}')
		return y2

眼下有两个问题,其一是每个样本句法树不同,因此需要即时地根据句法树生成大的模型架构,这似乎就不能批训练;其二是这种情况下,模型被划分为两部分,主模型部分是可以预先设定好的,但是句法树部分是随数据变化的,这就要同时训练多个模型,那么多个模型之间的梯度是否在torch中是可以传递的?其三,我看了一下句法树里的子女数,大部分都不超过5,有计划根据子女数再进一步细化模型。


20220907-20220908

  • 这两天在搬工位,从之前流动工位搬到现在博士的固定工位,重装工位上的电脑,把一些不想装在PC机上的软件都弄在工位上。有一说一实验室的电脑配置是真的好,院里这个钱还真是舍得花。
  • 昨天实在疼得难受,已经从四肢疼到前胸后背都发麻,偷个懒晚上没去训练,而且特别累,回宿舍11点就睡了。今天再偷懒就实在是说不过去了,尽管依然很疼,但是还是可以凭借意志克服酸痛,权且只跑了五圈,大腿和心肺都不太能耐受得住,想要在10月之前恢复到七八成,任重道远。

关于WIN10重置系统后会卡在开机要求设定账户的交互界面上的解决方案:
在这里插入图片描述其实在每一个位置都有可能卡住,只不过这里最容易卡,就是键盘输不了字,点左下角那个按钮调出软键盘也不行,然后就无限重启。

整了半天解决方案是这样的:

  • shift+F10可以调出cmd命令行
  • 输入taskmgr调出任务管理器,结束掉Microsoft IME任务
  • 此时就可以通过WIN+U调出设置界面(不结束Microsoft IME是不行的)
  • 这里就比较迷了,其实现在WIN+E也可以调出资源管理器,然后就可以正常使用了,但是你跳不过这个界面,所以还是需要去设置-账户 里设置新账户,一般来说这样就可以了,但是设置新账户的时候可能还是会卡,我的建议就是卡住就用任务管理器结束掉设置任务,然后多试几次,总是可以搞定的。

20220909-20220911

  • 股四头肌恢复了九成,至少走路不踉跄(其实这三天每天都还是5分配左右的慢跑在过渡),于是下午兴致冲冲去跑,然鹅又是不到5圈就熄火了,均配4’00",想去年连跑53圈不补给不休息,这么多天实力恢复了不到一成… 汗,倒是看到去年经常碰到一起跑的那个小伙子实力大增,也开始光着上半身裸奔,看步伐竟然是很稳定的内旋前脚掌,虽然腾空有点高不是很经济,但是的确是比去年强了好多,至少在这个学校里能前脚掌的人就已经是凤毛麟角了,敢裸奔总归是对自己的实力有点信心的。
  • 昨天中秋出去玩了大半天,错过了队内训练(以及东哥发的月饼),我说这帮人怎么这么卷,中秋节还顶着大太阳训练。发现不少博士(不只是我们院)平时是打羽毛球的,或许以后要转行咯(笑),老年人要有老年人的亚子。话虽如此,但是计划这个月还是抓紧恢复训练,15号争取有4~5km的体能,月底冲击10km。

提一个小问题,假如我想得到下面这棵树的一个层次遍历结果:

在这里插入图片描述
具体而言输入输出是这样的:

input: (ROOT (IP (NP (CP (IP (NP (DNP (ADJP (ADVP (AD 将)) (ADJP (JJ 传统))) (DEG 的)) (NP (NN 篇目))) (VP (ADVP (AD 首次)) (VP (VV 改为) (NP (NP (NN 名例) (PU 、) (NN 吏) (PU 、) (NN 户) (PU 、) (NN 礼) (PU 、) (NN 兵)) (PU 、) (NP (NR 刑)) (PU 、) (NP (NN 工各律)))))) (DEC 的))) (VP (VC 是))))
output: [
	[(ROOT)],
	[(IP)],
	[(PP, PU, NP, VP, PU)],
	[(P, NP), (,), (QP, NP), (ADVP, VP), (。)],
	[(根据), (NN, NN), (), (CD), (NN), (AD), (VV), ()],
	[(), (新闻), (报导), (大部分), (中学生), (都), (近视)]
]

这是一个简单的树层次遍历,但是输入并不是一棵树,而是字符串,你当然可以先把字符串变成树,但是我们希望最好只遍历input字符串一次即可得到output,注意output里的层次遍历结果必然满足后一层的()对数量恰好等于上一层非空节点的数量(比如最后一层有7个()对,倒数第二层的非空节点为[根据, NN, NN, CD, NN, AD, VV]共计7个)


20220912~20220913

  • 秋雨季,要下一周的雨,下午四点趁雨势小些去练了一会儿,恢复到3k的耐力,慢慢来呗。其实是队里例行训练,可是由于下雨都去风雨练力量了,然鹅我的腿还没有完全恢复,主要是髋还有点酸麻,懒得跑到风雨自取其辱了,据说连宋镇均都练吐了,我还是省省吧…

一个没啥用的小记录,关于Torch的优化器参数设置的一些方法,常规来说我们只有一个模型时直接把模型参数塞进去就行了:

from torch.optim import Adam

model = ... # 定义好的模型
optimizer = Adam(model.parameters(), lr=.01, weight_decay=.99)

但是有时候我们有多个模型需要同时训练,经过测试,可以直接把model.parameters()往参数列表里面塞,也可以把model.parameters()里的参数一个个往里面塞,具体看下面的例子(输出两次会发现参数值确实更新了):

import torch
from torch.nn import Module, Linear, CrossEntropyLoss, functional as F
from torch.optim import Adam

class Net(Module):
	def __init__(self, input_size, output_size):
		super(Net, self).__init__()
		self.linear = Linear(input_size, output_size)
	def forward(self, x):
		y = self.linear(x)
		output = F.softmax(y)
		return output
net1 = Net(2, 2)
net2 = Net(2, 2)
parameters = []
# parameters = [{'params': net1.parameters()}, {'params': net2.parameters()}]		# 此时net1的参数发生变化
# parameters = [{'params': net1.parameters()}]									# 此时net2的参数不变
for parameter in net2.parameters():
	parameters.append({'params': parameter})
for parameter in net1.parameters():
	parameters.append({'params': parameter})
optimizer = Adam(params=parameters, lr=.01, weight_decay=0)
loss_function = CrossEntropyLoss()
x = torch.FloatTensor([
	[64, 37],
	[55, 18],
	[53, 35],
	[78, 54],
	[56, 11],
	[-52, -21],
	[-54, -31],
	[-51, -41],
	[-66, -21],
	[-86, -51],
])
y = torch.LongTensor([1] * 5 + [0] * 5)
for i in range(10):
	y_ = net1(x)
	y_ = net2(y_)

	loss = loss_function(y_, y)
	loss.backward()
	optimizer.step()
	
	print(loss)
	for name, parameter in net1.named_parameters():
		print(name)
		print(parameter)
		print('-' * 64)

	for name, parameter in net2.named_parameters():
		print(name)
		print(parameter)
		print('-' * 64)

20220915~20220916

  • 晚上翘训练去给王炳杰指导数模,想不到都选C题,明明B题的飞机调度那么有趣,唉,C属于是大家都知道方法,就看谁文字写得好,过程更详实,B这种需要设计策略的属于是百家争鸣,才有意思nei。说起来王炳杰公管GPA第一,找了我们信管的两个队友,跟我说他的主要职责就是点外卖,好小子还挺实在。

对于可解释性衡量的几个维度:

  • 对输入进行扰动,如替换近义词,模型依然可以输出同样的预测,这个可以理解为是可解释性的检验(鲁棒性、敏感性)
  • 对可解释性进行攻击,adversarial attack
  • 经典的算法Integrated Gradient
  • DNN的可解释性是Fragile的,即非常脆弱,很困难
  • post-hoc interpretations: post-hoc interpretations assign an importance score to each token in the input which indicates its contribution to the model output. 这样看来interpretation可以是一个与输入序列(文本、图片)等长的向量,记录输入序列钟每个元素对预测结果的贡献度,这个方向的方法大致分为三类,gradient-based methods,reference-based methods,perturbation-based methods,
    也有分成两类的,即忽略reference: Gradient-based methods assume the model is differentiable and attempt to interpret the model outputs through the gradient information. Perturbation-based methods interpret model outputs by perturbing the input data.
    感觉reference-based的这个方法可能对参考文档中语句进行score的。但是感觉上一般会做检索
  • 在对话和问答中,这种可解释性相对要更直观一些,文本分类、句法解析里则相对困难
  • faithfulness metric: Sufficiency (SUFF) 以及 Decision Flip - Fraction of Tokens (DFFOT),等,衡量模型输出的置信度,但是这些指标很多情况得出的结论并不一致。
    • Decision Flip-Most Informative Token(DFMIT): 去掉这个token,模型输出就不一样了,那么赋值为1,否则为0(显然1比0要好)
      D F M I T = { 1 if c ( x ) ≠ c ( x \ x : 1 ) 0 if c ( x ) = c ( x \ x : 1 ) DFMIT = \left\{\begin{aligned} &1&&\text{if}c(x)\neq c(x\backslash x_{:1})\\ &0&&\text{if}c(x)= c(x\backslash x_{:1}) \end{aligned}\right. DFMIT={10ifc(x)=c(x\x:1)ifc(x)=c(x\x:1)
    • Decision Flip - Fraction of Tokens (DFFOT): 长为n的序列,最少删除k个token会使得结果发生变化,那么整个的得分为k/n(越小说明可解释性越令人信服)
      D F F O T = { min ⁡ k l x s.t. c ( x ) ≠ c ( x \ x : k ) 1 if c ( x ) = c ( x \ x : k ) ∀ k DFFOT = \left\{\begin{aligned} &\min \frac k {l_x}&&\text{s.t.}c(x)\neq c(x\backslash x_{:k})\\ &1&&\text{if}c(x)= c(x\backslash x_{:k}) \forall k \end{aligned}\right. DFFOT= minlxk1s.t.c(x)=c(x\x:k)ifc(x)=c(x\x:k)k
    • Comprehensiveness(COMP): 这个是越高越好, p就是模型的输出概率, 意思就是说如果去掉输入序列的重要的一部分,输出的概率变化越大越好。
      C O M P = 1 ∣ B ∣ ∑ q ∈ B ( p c ( x ) ( x ) − p c ( x ) ( x \ x : q % ) ) COMP = \frac1{|B|}\sum_{q\in B}(p_{c(x)}(x)-p_{c(x)}(x\backslash x_{:q\%})) COMP=B1qB(pc(x)(x)pc(x)(x\x:q%))
      一般 q ∈ B = { 1 , 5 , 10 , 20 , 50 } q\in B=\{1,5,10,20,50\} qB={1,5,10,20,50}
    • SUFF: 这个是越低越好,意思就是探究重要的token是否能够充分的包含整个序列的语义,因此希望输出概率变化的要小。
      S U F F = 1 ∣ B ∣ ∑ q ∈ B ( p c ( x ) ( x ) − p c ( x ) ( x : q % ) ) SUFF = \frac1{|B|}\sum_{q\in B}(p_{c(x)}(x)-p_{c(x)}(x_{:q\%})) SUFF=B1qB(pc(x)(x)pc(x)(x:q%))
    • Correlation between Importance and Output Probability (CORR): C O R R = − ρ ( μ , p ) CORR=-\rho(\mu, p) CORR=ρ(μ,p) μ \mu μ是降序排列的节点重要性, p p p是按这个顺序对应的去掉各个节点后模型的输出概率(感觉就是你得先用IG或者LIME得出各个token的重要性,然后再来用这些指标衡量)
    • MONO: 似乎就是CORR去掉一个负号

20220916~20220917

  • 昨天的量总算还能说的过去,2km+2km,间歇3分钟,配速3’57"和4’59",第一组没控好配速,跑完已经不太行了,第二组是跟着一个慢跑的人跑的,没有人带着指定坚持不下来,跑完心肺完全支撑不住,但是就这个间歇的情况而言,我基本已经有10圈的能力了。今天东哥说下周每个人专项测试,这就很局促,我想了想还是要去测一下的,毕竟已经翘了4次训练,上周是腿疼,这周就是纯懒,主要也是下午太晒,也不太想顶着这种太阳练,明后抱佛脚抓紧练,看看能不能突击5k。

TreeRNN已经完全实现,现在得问题在于非常慢,而且只能一个样本一个样本的训练,感觉上这种根据数据来配置模块结构的算法并不能实现很好的batch训练或者是并行,确实是有点头疼。


20220918~20220919

  • 一天发生了两件让我难受的事情,有点胃疼。算了,回实验室从头搞。
  • 昨天4’09"的配速干了十圈,以为青春又回来了,今天又被打回原型,慢慢来吧。lty催我去训练,我想我翘得确实是太久了。

不仅慢,而且还不收敛,属实给我整不会了。而且由于Codalab站点更新,原先的JEC-QA提交接口竟然不能用了,我想了想确实不能在一棵树上吊死,于是重新选了三个数据集再图发展,毕竟JEC-QA还是太小众,提交的人就不多,找个主流一些的或许好一些。

class TreeRNNEncoder(Module):
	"""2022/08/26 11:50:47
	为每个句法树节点建立独立的RNN网络, 输入为句法树节点对应下面的若干分词或从句的表示, 维数为(n_words, input_size), 输出为(output_size)
	:param args: QAModelConfig类型的配置
		   args.tree_rnn_encoder_tag_name			: 节点名称, setting.py中STANFORD_SYNTACTIC_TAG中定义的名称
													  这里区分是否为STANFORD_POS_TAG(叶子节点)
													  叶子节点必然汇入非叶节点, 非叶节点也汇入非叶节点
													  因此叶子节点的输入输出维数可以不同, 非叶节点的输入输出维数必须相同
		   args.tree_rnn_encoder_rnn_type			: 使用的RNN编码器, 可选值为RNN, LSTM, GRU
		   args.tree_rnn_encoder_num_layers			: RNN编码器堆叠的层数
		   args.tree_rnn_encoder_bidirectional		: RNN编码器双向标志
		   args.tree_rnn_encoder_squeeze_strategy	: 压缩RNN输出的strategy, 可选值有mean(取均值), final(只取最后一层输出), fixed_weight(固定权重加权平均), variable_weight(可变权重加权平均, 即作为参数训练)"""
	def __init__(self, args, tag_name):	
		super(TreeRNNEncoder, self).__init__()
		self.args = deepcopy(args)
		self.tag_name = tag_name
		self.embedding_layer = None										# 当且仅当使用顺序编码值且tag_name为词性标注时, 可以存在嵌入曾
		
		# 句法树根节点
		if self.tag_name == 'ROOT':						
			# tree_rnn_encoder_node_hidden_size -> tree_rnn_encoder_root_output_size
			self.input_size = self.args.tree_rnn_encoder_node_hidden_size
			self.output_size = self.args.tree_rnn_encoder_root_output_size
			self.sequence_encoder = eval(self.args.tree_rnn_encoder_rnn_type)(input_size	= self.input_size, 
																			  hidden_size	= int(self.output_size / (1 + self.args.tree_rnn_encoder_bidirectional)),	# 双向RNN的输出维数是hidden_size的2倍
																			  num_layers	= self.args.tree_rnn_encoder_num_layers,
																			  batch_first	= True, 
																			  bidirectional	= self.args.tree_rnn_encoder_bidirectional)
			
		# 词性标注: 即叶子节点, 使用DNN编码器
		elif self.tag_name in STANFORD_POS_TAG:		
			# embedding_size -> tree_rnn_encoder_node_hidden_size				
			if self.args.document_embedding is not None:
				# 文档嵌入: 禁用, 句法树模型只能使用词嵌入
				raise Exception('Please use word embedding rather than document model !')
			elif self.args.word_embedding is None:
				# 顺序编码值嵌入: 需要建立嵌入层
				self.input_size = self.args.default_embedding_size
				self.embedding_layer = Embedding(num_embeddings=pandas.read_csv(REFERENCE_TOKEN2ID_PATH, sep='\t', header=0).shape[0], 
												 embedding_dim=self.input_size)
			elif self.args.word_embedding == 'word2vec':
				# word2vec词嵌入
				self.input_size = self.args.size_word2vec			
			elif self.args.word_embedding == 'fasttext':
				# fasttext词嵌入
				self.input_size = self.args.size_fasttext
			else:
				raise Exception(f'Unknown word embedding: {self.args.word_embedding}')
			self.output_size = self.args.tree_rnn_encoder_node_hidden_size	
			self.sequence_encoder = Linear(in_features=self.input_size, 
										   out_features=self.output_size, 
										   bias=True)

		# 句法结构标注: 即非叶节点, 使用RNN编码器
		elif self.tag_name in STANFORD_SYNTACTIC_TAG:		
			# tree_rnn_encoder_node_hidden_size -> tree_rnn_encoder_node_hidden_size
			self.input_size = self.args.tree_rnn_encoder_node_hidden_size	
			self.output_size = self.args.tree_rnn_encoder_node_hidden_size	
			self.sequence_encoder = eval(self.args.tree_rnn_encoder_rnn_type)(input_size	= self.input_size, 
																			  hidden_size	= int(self.output_size / (1 + self.args.tree_rnn_encoder_bidirectional)),	# 双向RNN的输出维数是hidden_size的2倍
																			  num_layers	= self.args.tree_rnn_encoder_num_layers,
																			  batch_first	= True, 
																			  bidirectional	= self.args.tree_rnn_encoder_bidirectional)
		else: 
			raise Exception(f'Unknown syntactic tag: {tag_name}')
								
		# 2022/09/05 22:12:01 这里我想的是可以考虑在variable_weight的情况下引入一个Parameter类型的一维权重张量self.squeeze_weight
		# 2022/09/05 22:12:01 但是需要确保该张量的元素和为1, 这比较麻烦, 或许可以在损失函数中引入self.squeeze_weight的正则项, 但是这太麻烦了
		# 2022/09/05 22:12:01 于是我决定暂时不实现这种情况了
		if self.args.tree_rnn_encoder_squeeze_strategy in ['mean', 'final']:
			self.args.squeeze_weight = None
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'fixed_weight':
			# 2022/09/06 14:21:45 默认的固定权重定义为等比数列1/2, 1/4, 1/8, ...
			self.squeeze_weight = torch.Parameter(torch.FloatTensor([2 ** (-i) for i in range(self.args.default_max_child)]), requires_grad=False)
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'variable_weight':
			# 2022/09/06 14:21:45 即将上面默认的固定权重改为可修改(requires_grad=True)
			# self.squeeze_weight = torch.Parameter(torch.FloatTensor([2 ** (-i) for i in range(1, self.args.default_max_child)]), requires_grad=True)
			raise NotImplementedError
		else:
			raise Exception(f'Unknown squeeze_strategy: {self.squeeze_strategy}')
		
	def forward(self, x):
		"""2022/09/03 15:58:30
		前馈逻辑: 
		:param x: (batch_size, seq_len, input_size), 目前感觉batchsize或许只能取1"""
		
		# 需要进行嵌入的情况: (batch_size, seq_len) -> (batch_size, seq_len, input_size)
		if self.embedding_layer is not None:			
			x = self.embedding_layer(x)

		# (batch_size, seq_len, input_size) -> (batch_size, seq_len, (1 + bidirectional) * hidden_size)
		if self.tag_name in STANFORD_POS_TAG:		
			y1 = self.sequence_encoder(x)		
		elif self.tag_name in STANFORD_SYNTACTIC_TAG:
			y1, _ = self.sequence_encoder(x)	
		else:
			raise Exception(f'Unknown word embedding: {self.args.word_embedding}')
		
		# (batch_size, seq_len, (1 + bidirectional) * hidden_size) -> (batch_size, (1 + bidirectional) * hidden_size)
		if self.args.tree_rnn_encoder_squeeze_strategy == 'mean':
			y2 = torch.mean(y1, axis=1)			
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'final':
			y2 = y1[:, -1, :]
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'fixed_weight':
			sequence_length = x.shape[1]
			y2 = y1[:, 0, :] * self.squeeze_weight[sequence_length - 2]	# 初始位置的权重跟下一个位置的权重是相同的, 即1/2, 1/4, 1/8, 1/8
			for i in range(1, sequence_length):
				y2 += y1[: -i, :] * self.squeeze_weight[i - 1]
		elif self.args.tree_rnn_encoder_squeeze_strategy == 'variable_weight':
			# 2022/09/06 22:41:04 实现逻辑与fixed_weight的情况是一样的
			raise NotImplementedError
		else:
			raise Exception(f'Unknown squeeze_strategy: {self.squeeze_strategy}')
		return y2

20220920

  • 下午尽兴地训练,2k乳酸阈跑(4分配)+3×400米间歇+专训+2k节奏跑收尾,最后本来是跟宋镇均和王炳杰拉5k的,结果我5圈心肺就耐受不住了(4’25"的配速),不过宋镇均是真的拉垮了,他去年可是5km跑到19’50",然而之前四个月他没有跑过步,我好歹只是停了一个八月,但是为了周末的专项测试,都拼了,看到卢星雨外道也跑了10圈,配速大概是5’30",这些人体能怎么这么好,不都是被疫情关了两个月么。最后三天肯定是要疯狂拉体能了,周六要是5k测试都跑不下来未免太丢人了。
  • 报了锡马,11月6日开跑。碰碰运气呗,报不上不亏,报上血赚。
  • 其实我昨天难受的一件事情就是本来前天卢星雨在群里摇人去慢摇,结果我去的时候她已经跟别人在跑,有点扫兴就回实验室;今天训完本来也是同路去新食堂吃饭的,并没有别人一起,结果我口渴难耐一点胃口没有,又转道回实验室。大概是脑子进水了罢。

最近发现一个关于pandas的DataFrame很鸡毛的现象:

import pandas

df = pandas.DataFrame({'a': [1, 2, 3, 4], 'b': [2, 3, 4, 5]})
s = df.loc[0, :]

新建一个4×2的DataFrame,取第一行得到一个pandas.Series对象,得到的s['a']=1, s['b']=2

接下来我来修改s['a']的值:

s['a'] = 2
print(s['a'], type(s['a']))	# 2 <class 'numpy.int64'>

非常正常的结果,改成了2,自然输出是2;

那么我这样改呢?

s['a'] = [2]
print(s['a'], type(s['a']))	# ValueError: setting an array element with a sequence

可以发现,如果改成一个列表,会直接报错。

那么我这么改呢?

s['a'] = ['2']
print(s['a'], type(s['a']))	# ['2'] <class 'list'>

好家伙,又可以了,明明是可以改成列表的嘛!

那我要是改成更复杂的数据类型呢?

import torch
s['a'] = torch.LongTensor([8])
print(s['a'], type(s['a']))	# 8 <class 'numpy.int64'>

好家伙,我明明是改成了一个torch类型的张量,结果返回的竟然是numpy.int64类型的标量。

总之不要轻易赋值pandas.Series,鬼知道会给你搞个什么鸡毛出来。


20220921~20220924

  • 这三天疯狂加训。周三4’10"配速突破到7圈,周四又被打回原型,本来计划周五无论如何要慢跑拉体能,一来体能距离5km还差很远,二来临跑前不宜高配速拉强度。可是我还是特别地焦虑,周五仍然拉了强度,自然是五六圈就熄火了,之后就跑间歇把量补足。觉得要是跑不完5km可太丢人了。
  • 周六,我从早上起床就开始兴奋,真的真的好久没有跑过比赛了,可是下午三点才跑,午睡也睡不着,好在这几天熄灯比较早,状态养得差强人意。本以为就我和炳杰跑,后来开学初认识的会计研一李乐康也来,昨天测试只跑了23’23"的宋镇均也跟了一段,另外还有两个新队员,尽管搞得声势浩大,其实最后只有乐康、炳杰和我跑完了测试。第一圈乐康带了个1’17",我看情势不妙赶紧掉队,最终乐康19’27"(这个水平足以制霸现在的SUFE了),炳杰因为强行跟乐康导致后程跑崩,只跑了20’30",我的预期是21’30"~22’00",最终跑到21’20",均配4’16",算是超出预期了(2km分段8’07",4km分段16’58",我后面的确也是跑不动了,完全是靠意志坚持下来的,体能确实还是不够5km的)。

关于Albert模型的调用:

>>> from transformers import AlbertTokenizer, AlbertModel
>>> tokenizer = AlbertTokenizer.from_pretrained('albert-base-v2')
>>> model = AlbertModel.from_pretrained('albert-base-v2')
>>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
>>> outputs = model(**inputs)
>>> last_hidden_states = outputs.last_hidden_state

Transformers库对于不同模型的调用没有封装到一个统一的类里,这就搞得挺麻烦,调模型还得去查源码。codalab更新后JEC-QA彻底沦陷,不过现在看来这种以文档库作为外部参考的openQA确实太难了,大部分还是RC形式,参考段落至少是给定的。所以还是从RACE还有SQuAD入手比较靠谱。

关于TreeRNNEncoder训练慢且不收敛的问题,我想到一种解法是进行残差连接,暂未测试。


20220925~20220927

  • 前天约了一波凯爹,凯爹大概率要去中信研究所,这一年多前他先后在天风、海通、广发、中金、中信实习了个遍,如今终于把自己从头武装到脚趾,用时间堆出来的阅历的确是无可比拟的。
  • 从前天开始,东哥要求队员每天3000个跳绳,约凯爹回来后就开始跳,好久不跳,每次不到100个就要断,前天花了30分钟才跳完3000个,昨天就好多了,前1000个只断了一次,不过到后面体力不支还是容易断,但是只用了25分钟就完成了。看到卢星雨每天早上七点在群里打卡,就TM离谱,我现在一个人住最多也就七点半起,这种住校还能坚持早起的是真的狠人,顶级学霸确是有过人之处。
  • 今天下午快乐的400米间歇训练,一共8组,间歇2分钟,第1组1’17",2~6组全部1’20",最后两组1’23",东哥说我进步很大,我说只是大家都退步了才显得我有点儿进步(我的实力确实大不如去年),以前我都是被超的那个,今天每组我都能超掉前面两个人,抛开陈嘉伟这个变态(第1组带了个59"),只有王炳杰、宋镇均能比我快。有两个人最后都跑吐了,都没来得及到草丛里,直接就吐在跑到上。
  • 到下个月18号的运动会之前,我都会尽量跟上队里的训练,总是想参加一次运动会并且拿个名次的(至少5km要再次跑进20分钟),最后就是一场半马,对我来说这半道出家的体育之路就算是完满了。

前天给组里的胡振达做预答辩记录,发现几个前辈都太强了,且不说发了N多篇ACL EMNLP顶会的大师兄王明,两个17级的硕博胡振达和韩明月都是国奖获得者(每年博士也就两个人),也拿了不止一次一等学业奖学金,韩明月也有ACL入账,胡振达则是IJCAI,虽然次了一些但也足够毕业了。说起来王英林水平不咋地,这带的博士咋都怎么争气呢?

转头一看自己差不多就是组里的老大哥,顿时压力山大。胡振达做得是情感分析方向,尽管这个方向已经是日趋成熟,但是其实还是有很多值得深究的课题的,从预答辩的情况来看,我觉得胡振达做得已经是非常充分了,工作量也很大,专家也评价完全达到了博士学位论文的标准。其中有一些值得我借鉴的内容,权且做一个记录:

  1. 关于句法树的使用,相对来说在情感分析这种模型输入量比较小的问题上,做成分分析是更为必要的,也是更可能对模型产生增益的,成分分析在情感分析领域已经算是比较共识的方法了。我看到的情况是,可以以从句为级别对语句进行划分,这可能是比较有意义的。我以句法树的全标签进行划分,使用RNN一层一层的递进上去到根节点,结果就是①不能并行②模型参数量大③训练很慢。事实上只需要基于NP VP IP这些主要从句进行划分就足够了,接下来可以试试。
  2. 容易被battle的点,比如提出一些新概念,专家就会battle你是否前人已经提出过,相关工作如何如何。其实要提出一个全新的概念确实不太现实,大部分还是基于前人工作,从这个角度来看综述的确是很重要的,至少能堵上专家的嘴。
  3. 小波分析,一些序列预测问题可以考虑做序列分解,这个我倒不是特别清楚其中的原理,大约会有一些现成的包可以做,道理就是把序列分解成平稳和非平稳的部分,非平稳的部分大部分是高频数据,震荡数据,可以用随机量来替换,这样更有利于预测。
  4. 情感分析的三个级别,单词级别、句子级别、方面级别(aspect-wise),我看到胡振达做了一个多标注的矩阵,就是对于一个句子的每个单词进行情感标注,但是会用多个标注器标注,比如有的去识别反讽语气(这个也是目前情感分析研究的重点),有的去识别转折词,有的去识别情感极性,然后最后叠加得到最终的情感表征向量,这个其实就有点可解释性的味道了,虽然专家还是battle了他这个点,觉得可解释性并没有得到体现。
  5. 还有一个标注就是(s, r, 极性),比如通过NER识别出实体s和情感词r,然后标注两两对应的极性,这个可以通过一个矩阵来表示,这就是一个多标注问题,就是有的实体词会涉及多个单词,这种情况模型一般标注的就不太准。这种标注其实也可以看作是有一定可解释性的。

20220928~20221002

  • 上周四带炳杰跑10k,配速4’20",这货跑个三四公里就岔气,结果硬是跑成4+3+3的间歇,中间走一圈,炳杰是真的退化得太严重了。虽然我应该还是不能一口气跑完10km,但是这个月确实恢复得很好,距离能拿捏10km应该已经不远了。
  • 今天下午训练,又是8×400m的间歇,上周二间歇跑得不错(起手和结尾是1’17"和1’23",中间全部都是1’20"),今天状态也特别好,本以为可以轻松拿捏,结果第一组强追陈嘉伟跑出1’12"(我400米的pb都没有这么快),然后就直接废了,第二组1’25"竟然被卢星雨超了(虽然她是从第二组开始过来跟着跑的,但是被女生反超还是太丢人了),第三组1’22"结束已经站都站不住,被迫躺了一会儿,不过宋镇均更惨,第三个结束直接吐了,把管理室的水池给吐堵着了,跟他用盆一下一下给舀出来… 跳了第四组后面四组只能1’25"~1’30"摸鱼,死亡400真的名不虚传,宁可跑800m/1500m的间歇,也不跑400m间歇。

其实torch.utils.data里的dataset是有特别多不同的数据管道类型的,最常用的Dataset必须要把数据全部读入后才能使用(因为需要实现抽象类里的__getitem____len__方法),有时候很难将所有数据全部读进来,这时候就可以使用IterableDataset类,这个只需要实现__iter__方法即可,但是这个东西相对来说不如Dataset灵活,继承Dataset一般可以实现不同的管道,但是用继承IterableDataset通常只能实现一个管道,因为__iter__函数并不能重载。

其他还有很多很多不同的数据抽象类,包括什么非常特殊的推特数据集抽象类可以继承,具体可以参考Lib/site-package/torch/utils/data/dataset.py中的内容。


20221003~20221005

  • 今日份的训练是5×1000米,间歇4分钟。嘉伟没来,我估计他是跑伤了,30号一个半马,2号又是做8×400米的兔子,你跺你也麻。所以只有我跟宋镇均两条咸鱼跑,第一组几乎全力的3’26",二三四组都是3’45"~3’50",最后一组勉强冲回3’40",东哥给的要求是3’30"~3’40",讲道理这个要求并不是很高,是非常针对性的5000米训练,说到底还是太菜,要是嘉伟在估计就是往3’10"~3’20"猛干。
  • 跑完跟卢星雨摸了十圈,虽说是摸,但是最后两圈还是被卢星雨带到4’15"左右的配速,本来前面都是压着4’50"~5’00",结果这丫头不讲武德,冲刺那么快,我在外道居然还不太追得上去,菜得真实。
  • 其实昨天是状态特别好的,可惜风太大,逆风段根本冲不动,跑了3k就熄火了,完事追加1000个跳绳,顺便把双摇练了一下,现在能双摇个十次,感觉双摇消耗真的特别大。
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn

import os
import re
import time

from nltk.tokenize import word_tokenize, sent_tokenize, TreebankWordTokenizer
from nltk.tokenize.stanford import StanfordTokenizer
from nltk.parse.stanford import StanfordParser, StanfordDependencyParser

from setting import STANFORD_PARSER_MODEL_SUMMARY, LOGGING_DIR
from src.tool.util import timer, REGEXP_COMPILER


@timer
def load_word_tokenizer(source='nltk', language='english'):
	if source == 'nltk':
		tokenizer = lambda _text: word_tokenize(text=_text, language=language)
	elif source == 'treebank':
		tokenizer = TreebankWordTokenizer().tokenize
	elif source == 'stanford':
		tokenizer = StanfordTokenizer(path_to_jar=STANFORD_PARSER_MODEL_SUMMARY['jar']).tokenize
	else:
		raise NotImplementedError
	return tokenizer


@timer
def load_sentence_tokenizer(source='nltk', language='english'):
	if source == 'nltk':
		tokenizer = lambda _text: sent_tokenize(text=_text, language=language)
	else:
		raise NotImplementedError
	return tokenizer


@timer
def load_parse_tree_parser(source='stanford', language='english'):
	if source == 'stanford':
		parser = StanfordParser(STANFORD_PARSER_MODEL_SUMMARY['jar'],
										 STANFORD_PARSER_MODEL_SUMMARY['models'],
										 model_path=STANFORD_PARSER_MODEL_SUMMARY['pcfg'][language]).parse
	else:
		raise NotImplementedError
	return parser


@timer
def load_dependency_parser(source='stanford', language='english'):
	if source == 'stanford':
		parser = StanfordDependencyParser(STANFORD_PARSER_MODEL_SUMMARY['jar'],
				  STANFORD_PARSER_MODEL_SUMMARY['models'],
				 model_path=STANFORD_PARSER_MODEL_SUMMARY['pcfg'][language]).parse
	else:
		raise NotImplementedError
	return parser

20221006

  • 早上李乐康约我下午跑,我反正一天都在实验室随时可以奉陪。跟他边聊边跑干了5km,配速4’30",对他来说是很轻松,我就累得不行。发现他说话确实拽,但是仍然在可以接受的范围内。我想自己以前可能也有点像他这样,有点信口开河,啥都看不上的感觉,现在或许已经好多了,确实是要慢慢改。
  • 李乐康大概率18号要来跑校运会5000米,目前基本不可战胜的对手是陈嘉伟和李乐康,拼一拼有机会击败的是王炳杰,半斤八两的是宋镇均,目前新生的水平还不是很有把握,九月摸鱼跑的时候还是见到几个跑姿不错的兄弟的,尽量练到18号,打开20分钟,保五争三。说到底5000米还是太短了,如果有10000米的项目就算是陈嘉伟和李乐康都来也不是我的对手。

torch.DataLoader设置num_worker不为零的时候会调用多进程模块,此时迭代dataloader要写在if __name__ == '__main__'的条件分支下,如:

		if __name__ == '__main__':
			for iteration, data in enumerate(train_dataloader):
				optimizer.zero_grad()
				output = model(data)
				...

这个我特地去查了一下,其实只有windows平台需要这么写,Linux是无所谓的。摘了一段解释:

首先讲为什么Windows系统中使用多进程不加if name == 'main’会报错,这主要是因为python创建子进程的时候相当于会开辟新的一块内存空间去存储主进程中的代码,并且是通过导包的形式去复刻主进程中的代码的,那么这里就会有一个问题,如果不加if name == 'main’这个判断的话,那么执行代码的时候就会无限递归的创建子进程,但是multiprocessing.Process的源码中对无限递归的创建子进程这种行为是不允许的,所以就出现了报错提示,因此在windows系统中执行上面代码的时候就需要将创建子进程的代码放在if name == ‘main’ 这个判断下,这个时候子进程中通过导包的形式复刻的代码里面if name == 'main’这个判断会不成立,因为此时的 name 就不是 'main’了,在子进程中下面的代码也就不会被执行,那么最后也就不会报错了。

接着讲一下为什么相同的代码在Linux系统中不加if name == 'main’的话就不会报错,这是因为在Linux系统中有一个os.fork() 方法,这个方法会fork出一个子进程,在子进程中返回0,在父进程中返回子进程的进程编号,然后它们可以检查调用的返回值确定其状态,然后判断到底是父进程还是子进程,如果判断出是子进程,那么子进程就只继承必要的资源,也就是运行进程对象的目标方法所必须的资源,也就避免了递归的执行创建子进程的代码,所以在Linux系统中即使不加if name == 'main’也不会报错,而这个方法在window系统中是没有的,这就是以上所有问题的根本原因。

这样写确实挺蠢,而且这意味着带有迭代dataloader生成器的模块不可以被其他文件调用,否则__name__就不是__main__了,但是事实上设置num_worker大于1是很有帮助的,有显著的提升速度效果,具体看下面这个例子:

from torch.utils.data import Dataset, DataLoader
import time
import platform

print(platform.system())

class MyDataset(Dataset):

	def __init__(self):
		self.data = [
			{'id': '2020213101', 'name': 'lihao', 'age': 25},
			{'id': '2020213102', 'name': 'caoyang', 'age': 24},
			{'id': '2020213103', 'name': 'jack', 'age': 23},
			{'id': '2020213104', 'name': 'joe', 'age': 22},
			{'id': '2020213105', 'name': 'cathy', 'age': 29},
			{'id': '2020213106', 'name': 'tom', 'age': 28},
			{'id': '2020213107', 'name': 'john', 'age': 25},
			{'id': '2020213108', 'name': 'ben', 'age': 24},
		]


	def __len__(self):
		return len(self.data)

	def __getitem__(self, item):
		return self.data[item]


dataset = MyDataset()


def collate_fn(batch_data):
	time.sleep(2)
	for i in range(len(batch_data)):
		batch_data[i]['id'] = int(batch_data[i]['id'])
		batch_data[i]['name'] = batch_data[i]['name'].upper()
		batch_data[i]['age'] = batch_data[i]['age']
	return batch_data

dataloader = DataLoader(dataset=dataset,
						batch_size=2,
						num_workers=7,
						collate_fn=collate_fn,
						shuffle=False)


t = time.time()
for i in range(2):
	if __name__ == '__main__':
		for data in dataloader:
			print(data)
print(time.time() - t)

比如你在collate_fn中写了很长的逻辑(这里我用time.sleep(2)表示逻辑很长),那么num_workers=0时几乎就要耗时整整8秒(4个batch * 2秒),如果设置num_workers=1,则速度提升一倍,以此类推。这个例子同样说明了collate_fn的必要性,可能会有人觉得直接把collate_fn里的逻辑在迭代dataloader时直接实现不就好了,那样你就吃不到num_worker的时间红利了。


20221007~20221008

  • 根据历史经验,这篇差不多该收笔了。说得太多肯定不好。
  • 昨天主校区不太好,三门路很好。今天所有地方都不太好了。李乐康率先嗝屁,下午给他买了面包和水果送去,只过了一个小时,我也嗝屁了。
  • 组织水平还是一如既往地差,再等等,实在不行就设法润回去。可能今年的校运会也难以举办,11月13日扬州马拉松即将开启报名,如果报上我就退掉锡马,跑家门口的马拉松肯定是更合适一些的。

20221009~20221013

  • 果然不写点还是憋得慌。
  • fc网非常差,wifi基本等于没有,5g速度很很拉,昨天pre时扯着嗓子喊那一头才勉强听得清,不过没网某种意义上也是一件好事。
  • 前天进来累得不行,昨天又是早八到晚十二的事情,午觉都没睡成,困得要死。
  • 老妈最近也是身心俱疲,跟我抱怨说天天医院班都不上就测hs。也不想说太多,懂得都懂,不懂的早晚也轮到你头上,公道自在人心。已屏蔽50人以上的群聊,世界顿时清净许多。有能者无为,有心者无力。

RACE数据集管道:

class RACEDataset(Dataset):
	"""2022/09/21 18:35:19
	Pipelines for RACE dataset:
	@param args			: <class 'config.DatasetConfig'>
	@param mode			: The arrangement of 'train, dev, test' andd 'high, middle'. e.g. 'train_middle'.
	@param pipeline		: The pipeline name of dataset.
	@param is_debugging	: In debugging, only few lines of dataset will be sampled.
	@param kwargs		: Params for pipeline function."""
	pipelines = {'full_pretrain'		: 'self.full_pretrain_pipeline',
				 'tokenized_pretrain'	: 'self.tokenized_pretrain_pipeline'}
	def __init__(self,
				 args,
				 mode='train',
				 pipeline='shallow_pretrained',
				 is_debugging=False,
				 **kwargs):
		assert mode in ['train',	'train_middle',	'train_high',
						'dev',		'dev_middle',	'dev_high',
						'test',		'test_middle',	'test_high'], f'Unknown mode: {mode}!'
		assert pipeline in RACEDataset.pipelines, f'Unknown pipeline: {pipeline}'
		self.args = deepcopy(args)
		self.mode = mode
		self.pipeline = pipeline
		self.is_debugging = is_debugging
		# Run pipeline
		eval(RACEDataset.pipelines[self.pipeline])(**kwargs)

	# Warning: Need huge RAM as to usually unfeasible
	# Generate pretrained outputs of articles, questions and options
	@timer
	def full_pretrain_pipeline(self, tokenizer, model, multi_choice=False):
		logging.warn('shallow_pretrained_pipeline needs huge RAM!')
		# Load data
		# - tokenized_article_dataframe: [ 'id' | 'type' 'difficulty' 'article_sentence' 'article_token']
		# - tokenized_question_dataframe: [ index | 'id' 'answer' 'question_sentence' 'question_token' 'option_sentence' 'option_token' ]
		logging.info('Load data ...')
		tokenized_article_dataframe, tokenized_question_dataframe = self._load_RACE_dataframe(_article_import_path=RACE_TOKENIZED_ARTICLE_PATH, _question_import_path=RACE_TOKENIZED_QUESTION_PATH)
		# Process each field
		logging.info('Process pretrained article input...')

		tokenized_article_dataframe['article_input'] = tokenized_article_dataframe['article_sentence'].map(lambda _article_sentence: [generate_pretrained_model_outputs(texts=_paragraph_sentence,
																																										tokenizer=tokenizer,
																																										model=model,
																																										do_eval=True)[self.args.pretrained_outputs] for _paragraph_sentence in filter(None, _article_sentence)])	# Some paragraphs are empty lists in article_sentence, which should be filtered
		logging.info('Process pretrained question input...')
		tokenized_question_dataframe['question_input'] = tokenized_question_dataframe['question_sentence'].map(lambda _question_sentence: generate_pretrained_model_outputs(texts=_question_sentence,
																																										   tokenizer=tokenizer,
																																										   model=model,
																																										   do_eval=True)[self.args.pretrained_outputs])
		logging.info('Process pretrained option input...')
		tokenized_question_dataframe['option_input'] = tokenized_question_dataframe['option_sentence'].map(lambda _option_sentence: [generate_pretrained_model_outputs(texts=_choice_sentence,
																																									   tokenizer=tokenizer,
																																									   model=model,
																																									   do_eval=True)[self.args.pretrained_outputs] for _choice_sentence in _option_sentence])
		logging.info('Encode answer...')
		if multi_choice:
			# Multi choice: A-1, B-2, C-4, D-8
			tokenized_question_dataframe['answer'] = tokenized_question_dataframe['answer'].map(lambda _answer: sum([2 ** CHOICES_TO_INDEX[_choice] for _choice in _answer]))
		else:
			# Single choice: A-0, B-1, C-3, D-4 
			tokenized_question_dataframe['answer'] = tokenized_question_dataframe['answer'].map(lambda _answer: CHOICES_TO_INDEX[_answer])

		logging.info('Ready...')
		self.article_dataframe = tokenized_article_dataframe[['article_input']]
		self.question_dataframe = tokenized_question_dataframe[['id', 'answer', 'question_input', 'option_input']]
		logging.info('Done!')

	# Need much less RAM as to usually feasible
	# Generate pretrained tokenized inputs of articles, questions and options
	@timer
	def tokenized_pretrain_pipeline(self, tokenizer, multi_choice=False):
		# Load data
		# - tokenized_article_dataframe: [ 'id' | 'type' 'difficulty' 'article_sentence' 'article_token']
		# - tokenized_question_dataframe: [ index | 'id' 'answer' 'question_sentence' 'question_token' 'option_sentence' 'option_token' ]
		logging.info('Load data ...')
		tokenized_article_dataframe, tokenized_question_dataframe = self._load_RACE_dataframe(_article_import_path=RACE_TOKENIZED_ARTICLE_PATH, _question_import_path=RACE_TOKENIZED_QUESTION_PATH)
		# Process each field
		logging.info('Process pretrained article input...')
		tokenized_article_dataframe['article_tokenized_input'] = tokenized_article_dataframe['article_sentence'].map(lambda _article_sentence: [generate_pretrained_tokenization(texts=_paragraph_sentence, tokenizer=tokenizer) for _paragraph_sentence in filter(None, _article_sentence)])	# Some paragraphs are empty lists in article_sentence, which should be filtered
		logging.info('Process pretrained question input...')
		tokenized_question_dataframe['question_tokenized_input'] = tokenized_question_dataframe['question_sentence'].map(lambda _question_sentence: generate_pretrained_tokenization(texts=_question_sentence, tokenizer=tokenizer))
		logging.info('Process pretrained option input...')
		tokenized_question_dataframe['option_tokenized_input'] = tokenized_question_dataframe['option_sentence'].map(lambda _option_sentence: [generate_pretrained_tokenization(texts=_choice_sentence, tokenizer=tokenizer) for _choice_sentence in _option_sentence])
		logging.info('Encode answer...')
		if multi_choice:
			# Multi choice: A-1, B-2, C-4, D-8
			tokenized_question_dataframe['answer'] = tokenized_question_dataframe['answer'].map(lambda _answer: sum([2 ** CHOICES_TO_INDEX[_choice] for _choice in _answer]))
		else:
			# Single choice: A-0, B-1, C-3, D-4 
			tokenized_question_dataframe['answer'] = tokenized_question_dataframe['answer'].map(lambda _answer: CHOICES_TO_INDEX[_answer])
		logging.info('Ready...')
		self.article_dataframe = tokenized_article_dataframe[['article_tokenized_input']]
		self.question_dataframe = tokenized_question_dataframe[['id', 'answer', 'question_tokenized_input', 'option_tokenized_input']]
		logging.info('Done!')

	def _load_RACE_dataframe(self, _article_import_path, _question_import_path):
		_article_dataframe, _question_dataframe = load_RACE_csv(article_import_path=_article_import_path, question_import_path=_question_import_path, is_merged=False, is_debugging=self.is_debugging)
		_question_dataframe = _question_dataframe.merge(_article_dataframe[['id', 'type', 'difficulty']], how='left', on='id')
		_type_and_difficulty = self.mode.split('_')	# e.g. 'train_middle' can be splitted as 'train' and 'middle'
		if len(_type_and_difficulty) == 1:
			# train, dev, test
			_article_dataframe = _article_dataframe[_article_dataframe['type'] == _type_and_difficulty[0]]
			_question_dataframe = _question_dataframe[_question_dataframe['type'] == _type_and_difficulty[0]]
		else:
			# e.g. train_middle, train_high, so as dev and test
			_type, _difficulty = _type_and_difficulty
			_article_dataframe = _article_dataframe[(_article_dataframe['type'] == _type) & (_article_dataframe['difficulty'] == _difficulty)]
			_question_dataframe = _question_dataframe[(_question_dataframe['type'] == _type) & (_question_dataframe['difficulty'] == _difficulty)]
		_article_dataframe = _article_dataframe.set_index('id', inplace=False)
		return _article_dataframe, _question_dataframe.reset_index(drop=True)

	def __getitem__(self, item):
		question_dataframe_slice = self.question_dataframe.loc[item, :]
		article_dataframe_slice = self.article_dataframe.loc[question_dataframe_slice['id'], :]
		return article_dataframe_slice, question_dataframe_slice
		
	def __len__(self):
		return self.question_dataframe.shape[0]

20221014~20221015

  • 流程差不多已经走完了,预期16号三门路解,17号主校区解,18号返校,21号完全解放。这两天有点养猪,学校矫枉过正送了一堆零食。但是又不能剧烈跳动,只好深蹲和床上练腰腹了。
  • 但愿接下来一路顺风,我本来是准备直接备战下个月半马了,这样看来校运会还能赶得上,拿起地上还没拆封的24瓶农夫山泉开始做深蹲,再这么养猪下去指定是要跪了 …

关于masked_fill:

a = torch.randn(5,6)

x = [5,4,3,2,1]
mask = torch.zeros(5,6,dtype=torch.float)
for e_id, src_len in enumerate(x):
    mask[e_id, src_len:] = 1
mask = mask.to(device = 'cpu')
print(mask)
a.data.masked_fill_(mask.byte(),-float('inf'))
print(a)
----------------------------输出
tensor([[0., 0., 0., 0., 0., 1.],
        [0., 0., 0., 0., 1., 1.],
        [0., 0., 0., 1., 1., 1.],
        [0., 0., 1., 1., 1., 1.],
        [0., 1., 1., 1., 1., 1.]])
tensor([[-0.1053, -0.0352,  1.4759,  0.8849, -0.7233,    -inf],
        [-0.0529,  0.6663, -0.1082, -0.7243,    -inf,    -inf],
        [-0.0364, -1.0657,  0.8359,    -inf,    -inf,    -inf],
        [ 1.4160,  1.1594,    -inf,    -inf,    -inf,    -inf],
        [ 0.4163,    -inf,    -inf,    -inf,    -inf,    -inf]])

20221016~20221018

  • 本来预告是晚上六点半撤离fc,结果到九点半上车,十点半才到宿舍,车晃得要死,后面背着书包,前边拿着睡袋,挤得不行,幸好吃的不多,差点没把我给晃吐。
  • 盘点一下学校送的大礼包:桶装薯片×2,袋装薯片(小)×4,威化饼干×1,火腿肠×1,锅巴×1,草莓干×1,压缩饼干×1,泡面×1,散称巧克力×4,散称肉铺×2,切片面包×1,百事可乐500ml×1,王老吉×1,八宝粥×1,午餐肉×1,榨菜×1,袋装牛肉粒×1。然后我因为第一天没拿到这个大礼包,第二天填了两遍申请表也没送到,最后给我送了整整两倍的量,今晚回到三门路还送了6桶泡面+2桶薯片+1箱莫斯利安,学校这次是真的下血本了,可能学校也是觉得自己很对不起学生罢。
  • 7天除盒饭外一共吃了2桶薯片,1包爆米花,2桶泡面,2袋牛肉粒,真心是忍不住想吃,最后两天是强忍着没吃零食,然后现在脸上发痘子,活该。王炳杰居然一个零食也没吃,自律的学霸是多么可怕。
  • 接下来必须疯狂加练,争取每天十二点前能睡,把状态完全养回来。第一场半马,又是家门口的比赛,我是奔着130的目标(4’14"/km,90分钟)去练的,这10天禁闭属实把我心态搞炸了,三月底也是一样,每次都是我恢复到接近巅峰,突然就断了,又不得不从头再练,这种环境下想全盛状态跑一次比赛真的奢侈,但是我每天都在期盼着这一天的到来。

关于transformer中各个模型的分词器,没有全部测试,主流的几个模型都差不多:

tokenizer(texts, return_tensors='pt', truncation=True, padding=True, max_length=32)

padding一般都是True,在一些模型里padding不为True,是直接报错的

truncation与max_length是同步使用的,即truncation=True时,max_length才有效,此时会截断texts(一系列句子构成的列表),但是注意并不一定都是截断到max_length,比如texts中最长的都不到32(比如只有30),那么就只会截断到30,这个体现在tokenizer的输出以及最终model输出的last_hidden_state中(即last_hidden_state的第二维是min(32, 30))

这里就有一个问题,你设置的max_length其实并没有生效,函数逻辑还是按照texts中最长句子的length作为max_length,所以得到输出结果还得自己做padding,这就显得挺蠢,但是似乎也没什么好办法。


20221019

  • 下午手头事情做完就溜出去开跑,确实很松,回来时虽然没有进校码,反正随便找个人帮忙截个图也能进得来。5.58km,4’14"配速,看起来还挺快,其实是中途红绿灯都停下来休息了一会儿。之前穿这双新买的飞燃PB,每次都磨破脚趾,今天穿了双最厚的袜子,前脚掌跑到最后还是磨得生疼。我看好多大佬评测飞燃PB虽然不是顶级碳板,但总归是能用,比赛也有高手会穿,可能确实人各有异,脚型、跑法不同,感觉可能更适合后脚跟跑法,我肯定以后都是用前脚掌跑,这双飞燃PB真的血亏。
  • 去年买的zoomx两个鞋头都破洞了,虽然也不影响跑。昨天看了一下鞋子,感觉特步3.0pro价位比较能接受。zoomx到底还是太贵,虽然确实一流竞速碳板,出成绩的不二之选,但是性价比真的一般,寿命太短,关键过了一年多还是那个价,太TM贵了。
  • 李乐康早上六点多就出去跑,但是跑炸,早上七点半来问我晚上跑不跑,其实我很想让他给我上上强度,但是这两天还是循序渐进,不能给他带乱节奏,校运会或许还有可能参加完再回家,到时候再见分晓,至少不能被他套圈了再。

最近带着在复习转博考,看到一个很有趣的规划转换,好像之前没注意过:

问题:求解一元偶次多项式的最小值。
minimize x 2 n + a 1 x 2 n − 1 + . . . + a 2 n − 1 x + a 2 n \text{minimize}\quad x^{2n}+a_1x^{2n-1}+...+a_{2n-1}x+a_{2n} minimizex2n+a1x2n1+...+a2n1x+a2n

首先这个问题可以先写成:
maximize t subject to x 2 n + a 1 x 2 n − 1 + . . . + a 2 n − 1 x + a 2 n − t ≥ 0 \begin{aligned} &\text{maximize}&&t\\ &\text{subject to}&&x^{2n}+a_1x^{2n-1}+...+a_{2n-1}x+a_{2n}-t\ge0 \end{aligned} maximizesubject totx2n+a1x2n1+...+a2n1x+a2nt0

即要求 x 2 n + a 1 x 2 n − 1 + . . . + a 2 n − 1 x + a 2 n − t x^{2n}+a_1x^{2n-1}+...+a_{2n-1}x+a_{2n}-t x2n+a1x2n1+...+a2n1x+a2nt是非负多项式。

这里有一个很重要的引理,就是一个偶次单变量多项式在 R \R R上非负,当且仅当它可以写程若干多项式的平方和。

这个引理的证明可以简单用下面的式子来说明:
p ( x ) = ∑ i = 0 2 n p i x i = p 2 n ∏ j = 1 r ( x − λ j ) 2 m j ∏ i = 1 s ( x − μ i ) ( x − μ j ∗ ) p(x)=\sum_{i=0}^{2n}p_ix^i=p_{2n}\prod_{j=1}^r(x-\lambda_j)^{2m_j}\prod_{i=1}^s(x-\mu_i)(x-\mu_j^*) p(x)=i=02npixi=p2nj=1r(xλj)2mji=1s(xμi)(xμj)

其中 λ j \lambda_j λj p ( x ) p(x) p(x)的所有实数根(重数必然为偶数,否则穿越坐标轴就有负值了), ( μ i , μ i ∗ ) (\mu_i,\mu_i^*) (μi,μi)是成对的共轭复数根。

首先后半部分 ( x − μ i ) ( x − μ i ∗ ) = ( x − ℜ ( μ i ) ) 2 + ( ℑ ( μ i ) ) 2 (x-\mu_i)(x-\mu_i^*)=(x-\Re(\mu_i))^2+(\Im(\mu_i))^2 (xμi)(xμi)=(x(μi))2+((μi))2,两个标记 ℜ \Re ℑ \Im 表示复数的实部和虚部,也就是说总是可以写成平方和的形式。然后平方和的函数相乘依然可以写成平方和。

前半部分显然是一个平方,因此引理得证。

于是非负多项式 x 2 n + a 1 x 2 n − 1 + . . . + a 2 n − 1 x + a 2 n − t x^{2n}+a_1x^{2n-1}+...+a_{2n-1}x+a_{2n}-t x2n+a1x2n1+...+a2n1x+a2nt可以写成平方和形式:
x 2 n + a 1 x 2 n − 1 + . . . + a 2 n − 1 x + a 2 n − t = ∑ i = 1 k ( ∑ j = 0 n z i j x j ) 2 = ∑ i = 1 k [ 1 , x , x 2 , . . . , x n ] z i z i ⊤ [ 1 , x , x 2 , . . . , x n ] ⊤ = [ 1 , x , x 2 , . . . , x n ] Z [ 1 , x , x 2 , . . . , x n ] ⊤ \begin{aligned} &x^{2n}+a_1x^{2n-1}+...+a_{2n-1}x+a_{2n}-t\\ =&\sum_{i=1}^k\left(\sum_{j=0}^nz_{ij}x^j\right)^2\\ =&\sum_{i=1}^k[1,x,x^2,...,x^n]z_iz_i^\top[1,x,x^2,...,x^n]^\top\\ =&[1,x,x^2,...,x^n]Z[1,x,x^2,...,x^n]^\top \end{aligned} ===x2n+a1x2n1+...+a2n1x+a2nti=1k(j=0nzijxj)2i=1k[1,x,x2,...,xn]zizi[1,x,x2,...,xn][1,x,x2,...,xn]Z[1,x,x2,...,xn]
其中 z i = [ z i 1 , . . . , z i n ] z_i=[z_{i1},...,z_{in}] zi=[zi1,...,zin] Z = ∑ i = 1 k z i z i ⊤ ⪰ 0 Z=\sum_{i=1}^kz_iz_i^\top\succeq0 Z=i=1kzizi0

最后原问题被改写为下面的半正定规划:
maximize t subject to a 0 − t = Z 1 , 1 a 2 k = ∑ i + j = k + 2 Z i , j , k = 1 , . . . , 2 n − 1 Z n , n = 1 Z ⪰ 0 \begin{aligned} &\text{maximize}&&t\\ &\text{subject to}&&a_0-t=Z_{1,1}\\ &&&a_{2k}=\sum_{i+j=k+2}Z_{i,j},k=1,...,2n-1\\ &&&Z_{n,n}=1\\ &&&Z\succeq0 \end{aligned} maximizesubject tota0t=Z1,1a2k=i+j=k+2Zi,j,k=1,...,2n1Zn,n=1Z0

虽然问题变量规模很大( O ( n 2 ) O(n^2) O(n2)),但是成功转为了SDP形式,实在是妙不可言。


20221020

  • 下午四点路跑,换了双薄底鞋准备有氧慢跑。5’04"的配速干了6km,慢且轻松的随机游走,不识路,不知路在何处,不知前路是何风景,要不是现在耐力不行,我能这样有氧跑半天。路上遇到3个跑步的大叔,分别跟了一小段,跟其中一个打了个招呼,很巧,也是在备赛马拉松的同好。今晚是队里例行训练,仍然无法参加,只能自己做一些恢复训练。
  • 禁闭期间跟sxy聊了不少,感慨颇多。大家压力都很大,然而逃避总不是个出路,最终仍然只能向生活妥协,所以我很想在妥协前尽可能多地去再做一些疯狂的事情。跑一次越野,学会游泳去完成一次铁三,完成一次长途骑行。
  • 事实上19年底后就不曾再见,这么多年来却一直藕断丝连地在联系。三年过去都老年人了,感觉没精力也没兴趣去暧昧,平淡叙事与意见交换,或许只是我想得太多。禁闭回来之后事情就特别多,估计sxy也很忙,也懒得去叨扰,顺其自然呗。想象一下也知道现在处对象也超累(虽然感觉以后谈会更累),还不如跑步来得快乐,活在当下。

self.register_buffer('pos_table', torch.tensor(numpy.array([[1., 2., 3.], [4., 5., 6.]])))

  • 在继承torch.nn.module的类中进行定义一个成员变量self.pos_table;
  • 该成员变量self.pos_table将不视为model.parameters();

20221021~20221022

  • 昨日加码,反正有人三餐投喂,小腿也酸得很,老老实实宅一天,不敢偷溜出去惹事了。
  • 下午训练。6×1000米,陈嘉伟、王炳杰、宋镇均还有我,东哥给的目标是每组3’35"~3’45"。不负众望,6组全部垫底,但也没有被拉爆太多,3’29" → \rightarrow 3’39" → \rightarrow 3’53" → \rightarrow 3’56" → \rightarrow 4’07"(这组摸了,我跟嘉伟说这要是最后一组,还能尽全力去冲,一想到这才倒数第二组,我腿就软了,嘉伟看我可怜就慢跑带了一组,其实我意下要不跑个5组拉倒了呗) → \rightarrow 3’41"(强弩之末)。
  • 宋镇均忽然神勇(尽管又跑吐),每次都能跑到第二,王炳杰就萎得不行,每次一圈多就岔气跑不动,最后直道我都能几乎追得上他。另外还有一个兄弟也跟了6组,不过实在是拉胯,第一组跑到4分半,最后都掉到5分钟,第一组卢星雨都能拉他一个直道(然后卢星雨就摸了)。
  • 零食全部送掉,十几包泡面,若干(桶)袋薯片,午餐肉和火腿,还有4瓶可乐(其实有两瓶是无糖的,今天练完就后悔,特别想喝),赛前一定要规律饮食!不熬夜!不能再乱吃了啊啊啊!!!(然鹅昨天夜宵又吃了一包泡面,自制力真差)

经典Comatching的核心idea(机器阅读理解,arxiv@1806.04068):

  • P ∈ R d × P , Q ∈ R d × Q , A ∈ R d × A {\bf P}\in\R^{d\times P},{\bf Q}\in\R^{d\times Q},{\bf A}\in\R^{d\times A} PRd×P,QRd×Q,ARd×A
  • 序列编码:
    H p = Bi-LSTM ( P ) ∈ R l × P H q = Bi-LSTM ( Q ) ∈ R l × Q H a = Bi-LSTM ( A ) ∈ R l × A {\bf H}^p = \text{Bi-LSTM}({\bf P})\in\R^{l\times P}\\ {\bf H}^q = \text{Bi-LSTM}({\bf Q})\in\R^{l\times Q}\\ {\bf H}^a = \text{Bi-LSTM}({\bf A})\in\R^{l\times A} Hp=Bi-LSTM(P)Rl×PHq=Bi-LSTM(Q)Rl×QHa=Bi-LSTM(A)Rl×A
  • 注意力权重:
    G q = SoftMax ( ( W g H q + b g ⊗ e Q ) ⊤ H p ) ∈ R Q × P G a = SoftMax ( ( W g H a + b g ⊗ e Q ) ⊤ H p ) ∈ R A × P H ˉ q = H q G q ∈ R l × P H ˉ a = H a G a ∈ R l × P W g ∈ R l × l , b g ∈ R l , e Q ∈ R Q \begin{aligned} {\bf G}^q &= \text{SoftMax}(({\bf W}^g{\bf H}^q+b^g\otimes e_Q)^\top{\bf H}^p)\in\R^{Q\times P}\\ {\bf G}^a &= \text{SoftMax}(({\bf W}^g{\bf H}^a+b^g\otimes e_Q)^\top{\bf H}^p)\in\R^{A\times P}\\ \bar {\bf H}^q &= {\bf H}^q {\bf G}^q\in\R^{l\times P}\\ \bar {\bf H}^a &= {\bf H}^a {\bf G}^a\in\R^{l\times P}\\ {\bf W}^g&\in\R^{l\times l},b^g\in\R^{l},e^Q\in\R^Q \end{aligned} GqGaHˉqHˉaWg=SoftMax((WgHq+bgeQ)Hp)RQ×P=SoftMax((WgHa+bgeQ)Hp)RA×P=HqGqRl×P=HaGaRl×PRl×l,bgRleQRQ
  • 将passage状态与问题以及候选答案进行匹配:
    M q = ReLU ( W m [ H ˉ q ⊖ H p H ˉ q ⊗ H p ] + b m ) ∈ R l × P M a = ReLU ( W m [ H ˉ a ⊖ H p H ˉ a ⊗ H p ] + b m ) ∈ R l × P C = [ M q M a ] ∈ R 2 l × P W g ∈ R l × 2 l , b g ∈ R l \begin{aligned} {\bf M}^q&=\text{ReLU}({\bf W}^m\left[\begin{matrix}\bar {\bf H}^q\ominus{\bf H}^p\\\bar {\bf H}^q\otimes{\bf H}^p\end{matrix}\right]+b^m)\in\R^{l\times P}\\ {\bf M}^a&=\text{ReLU}({\bf W}^m\left[\begin{matrix}\bar {\bf H}^a\ominus{\bf H}^p\\\bar {\bf H}^a\otimes{\bf H}^p\end{matrix}\right]+b^m)\in\R^{l\times P}\\ {\bf C}&=\left[\begin{matrix}{\bf M}^q\\{\bf M}^a\end{matrix}\right]\in\R^{2l\times P}\\ {\bf W}^g&\in\R^{l\times 2l},b^g\in\R^{l} \end{aligned} MqMaCWg=ReLU(Wm[HˉqHpHˉqHp]+bm)Rl×P=ReLU(Wm[HˉaHpHˉaHp]+bm)Rl×P=[MqMa]R2l×PRl×2l,bgRl
  • C {\bf C} C的一个列向量即为一个co-matching state,用于表示文章中的一个token与问题及选项的匹配情况。

20221023~20221024

  • 下午4’20"跑了一组6000米,本着10000米去跑的,但是耐力明显不够上到25圈。整体上感觉途中速度掉得很厉害,第一个1000米只用了4’05",后面都快掉到4分半了,步幅1米4,步频165,心率181,这两天休息得很好,很在状态,所以有点失望。不过已经快7个月没有跑过10km了,还是不能太心急,明天训练再接着顶,至少今天过5km的时候明显觉得不吃力,虽然整体配速还是不够理想。
  • 事情全堆到一起,既要准备转博考,又要准备马拉松和校运会,还有进度要赶,但是这次对我来说考试可以补考,进度可以通宵赶,但是运动会错过就是一辈子的遗憾了。最后两周,至少要练到接近去年年底的全盛状态。

总结一下MRC模型的一个发展历史:目前一个主流的思想仍然是在passage,question和option之间做匹配


20221025~20221027

  • 前两天被恶心了,气得要命,状态搞烂,事情又没做成,真的不想说这种烂事。
  • 状态不好,但是这两天都没怎么练,所以今晚本来在等人过来带我拉个长距离,结果宋镇均回家,王炳杰说晚上训练容易失眠就不来了,陈嘉伟更好,直接悄无声息地翘了,大伙都说是不是感情受挫,我觉得不太可能,感情受挫不是该练得更猛?
  • 结果变成我带一个学弟和卢星雨匀速跑(因为我已经是剩下跑得最快的了…),结果那个学弟连卢星雨都跑不过,我带的4’20"配速,他2km不到就被拉爆,卢星雨跟了我3km,我自己到4km就停了,操场人太多不想认真跑,后面有个女生跟着还有点动力,一个人跑,状态又差,太容易疲劳。之后练了若干组力量(40~60kg深蹲,大腿后侧离心和向心,超级波比跳,腰腹组合性训练),好久没有练过力量了,拉垮得不行。
def screenshot(): 	
	with open('temp/screenshot.log', 'w') as f:
		pass
	global count, image
	print('Prepare for Screenshot ...')
	time.sleep(2)
	image = pyautogui.screenshot()
	image = cv2.cvtColor(numpy.asarray(image), cv2.COLOR_RGB2BGR)
	count = 0
	def _on_move(x, y):
		return True
	def _on_click(x, y, button, pressed):
		global count, image
		if pressed:
			return True
		count += 1
		with open('temp/screenshot.log', 'a') as f:
			f.write(f'{x}\t{y}\n')
		if count < 2:
			return True
		with open('temp/screenshot.log', 'r') as f:
			lines = f.read().splitlines()
		x1, x2 = list(map(int, lines[0].split('\t')))	
		y1, y2 = list(map(int, lines[1].split('\t')))	
		image = image[x2: y2, x1: y1, :]
		cv2.imwrite(f'D:/image-{time.strftime("%Y%m%d%H%M%S")}.png', image)
		return False
	def _on_scroll(x, y, dx, dy):
		return True
	with mouse.Listener(on_move=_on_move, on_click=_on_click, on_scroll=_on_scroll) as listener:
		listener.join()

20221028

  • 晚上吃饭碰到王炳杰才恍然大悟,原来陈嘉伟、陈发端全在谈恋爱,怪不得一个个训练不积极。然后炳杰跟我说他也刚谈,我猛地一惊,队里单身狗肯定是不多了(至少还有个卢星雨,她是优秀到让人望而却步。假小子气十足的全能超级学霸,在男生中兄弟相称,女生中学霸大姐姐)。
  • 我问他是不是跟他之前喜欢的那个女生,他说是一个喜欢他的女生。说第二天他就后悔了,觉得初恋应该是很重要的,有点随便。而且两人都很忙,跟以前普通朋友也没啥区别。然后他又吐槽了一番,觉得不该谈恋爱,太浪费时间,应该好好学习,等研究生再谈,都怪陈嘉伟、陈发端之流带坏了风气,害他也去谈恋爱了。我说等你读研你就又不这么觉得了[迫真]。
  • 扬马没中签,锡马又延期,校运会推迟到15号,虽然缓冲空间长了许久,但是大概今年又要凉了。新买的zoomx next%2到货,还没拆封,到头来我还是不敢买没穿过的鞋子,耐克贵归贵,但是确实跑得快,比赛不二之选。另外买了一双炳杰一直穿的361飞飙用于日常训练。
  • 感性地讲想谈恋爱,理性地说又不是那么想谈,当我看到大二大三的小朋友都在开始谈,有点看到以前的影子,怀念又伤感,罢了。对我来说恋爱已经完全不是两三年前的概念,可有可无,当然我也没有几个选择。靠的太近会是一种束缚,但是我终究无法选择自由终生。

转博考复习,备忘记录:

  • 费雪信息量独立同分布情况只要计算一个样本点的pdf的信息量再乘以 n n n即可
  • 几何分布 f ( x ) = p ( 1 − p ) x − 1 f(x)=p(1-p)^{x-1} f(x)=p(1p)x1,有 E ( X ) = 1 / p , Var ( X ) = ( 1 − p ) / p 2 \mathbb{E}(X)=1/p,\text{Var}(X)=(1-p)/p^2 E(X)=1/p,Var(X)=(1p)/p2
  • P ( X > μ + x ) ≤ σ 2 x 2 + σ 2 , ∀ x P(X>\mu+ x)\le\frac{\sigma^2}{x^2+\sigma^2},\forall x P(X>μ+x)x2+σ2σ2,x,这个我想了半天还是不知道怎么用切比雪夫证( X X X的期望方差为 μ , σ 2 \mu,\sigma^2 μ,σ2
  • 工具变量,二阶段最小二乘(还没细看)
  • 离散形式的二元变换pmf直接代入替换形式即可,注意要把所有符合的情况累加,比如有不止一组 ( x , y ) (x,y) (x,y)可以计算得到同一组 ( u , v ) (u,v) (u,v),那么需要把这些情况全部累加。连续形式乘以 ∣ J ∣ |J| J即可。
  • MLE,注意参数范围,比如均匀分布的MLE就是 max ⁡ x i \max x_i maxxi,求导求不出来的。

20221029

  • 下午训练,嘉伟要准备明早的高百比赛,一个人去练长距离,我跟王炳杰继续6×1000米,正好试试刚穿nike zoomfly next% 2(经过对比,外型上跟去年买的一模一样,应该是正品,抵价券后只用¥997就到手,目前官方标价还是¥1599,差强人意)。3’26" 3’42" 3’44" 3’43" 3’43" 3’30",全部跑进345,大有进步!最后一组甚至超了王炳杰30米,说明鞋子应该质量还行,希望不要两三百公里就断碳板。不过炳杰最近刚谈恋爱,肯定茶饭不思,睡得也不好,算是趁人之危,胜之不武了。
  • 跑完跟炳杰进行情感咨询。第一次谈就开始考虑以后可能的异地恋,乃至谈婚论嫁的话题,他说对方就是本着结婚为目的来谈的,炳杰也觉得可行,而且异地恋也没啥,他说他肯定不会主动提分手(巧了,我也是这么想的)。我说这太理想,像我们练长跑的人,总觉得没有什么事情是坚持不下来的,人和人总是能慢慢磨合。恋爱的开始总是非常美好,但是终究要归于平淡,热身的时候嘉伟说他已经快一周没见过女朋友了,估计是不知道在一起该做啥,就干脆避而不见。
  • 他说今天晚上要官宣。我跟炳杰提了两点,第一,男生一定要主动,该牵手牵手,该果断果断,说话做事别模棱两可,说话声音明亮一些少自言自语(其实都是我自己以前的教训…),虽然比较形式主义,但是能给对方一些可靠的感觉。第二,注意对方的习惯与偏好,小毛病能改则改,我以前经常被wyy说驼背,还有衣服洗得不干净之类的(有点老母亲教训儿子),现在也慢慢改过来,洗衣服要加点消毒液。其实这些无伤大雅,但是日积月累可能就是导火索。我当然希望炳杰能谈得长久,但是第一场往往都会以刻骨铭心的痛苦告终,能一场走到终老的可能是童话故事里才会有的桥段罢。

转博考备忘:

  • 梯度下降法:下降方向是 ∇ x k \nabla x_k xk
  • 牛顿法:下降方向是 ( ∇ 2 f ( x ) ) − 1 ∇ f ( x ) (\nabla^2 f(x))^{-1}\nabla f(x) (2f(x))1f(x),注意要求海森矩阵的逆,虽然收敛速度要比梯度下降快,但是每一步的计算量要更大
  • 拟牛顿法:改进需要矩阵求逆的劣势,改为迭代形式近似海森矩阵的逆,设 p k = x k + 1 − x k , q k = ∇ f ( x k + 1 ) − ∇ f ( x k ) p_k=x_{k+1}-x_{k},q_k=\nabla f(x_{k+1})-\nabla f(x_k) pk=xk+1xk,qk=f(xk+1)f(xk),则 q k ≈ ∇ 2 f ( x k + 1 ) p k q_k\approx \nabla^2f(x_{k+1})p_k qk2f(xk+1)pk,于是 p k ≈ ∇ 2 f ( x k + 1 ) − 1 q k p_k\approx \nabla^2f(x_{k+1})^{-1}q_k pk2f(xk+1)1qk,这样就得到了拟牛顿条件 p k = H k + 1 q k p_k=H_{k+1}q_k pk=Hk+1qk,即构造出 ∇ 2 f ( x k ) − 1 \nabla^2 f(x_k)^{-1} 2f(xk)1的一个近似矩阵 H k H_k Hk
  • 拟牛顿法的秩一矫正:假设 H k + 1 = H k + Δ H k H_{k+1}=H_k+\Delta H_k Hk+1=Hk+ΔHk,为满足 H H H为正定矩阵要求,假设矫正矩阵 Δ H k = α z k z k ⊤ \Delta H_k=\alpha z_kz_k^\top ΔHk=αzkzk,其中, z k z_k zk为列向量。由此可以得到: p k = H k q k + α k z k z k ⊤ q k p_k=H_kq_k+\alpha_k z_k z_k^\top q_k pk=Hkqk+αkzkzkqk,于是 z k = p k − H k q k α k z k ⊤ q k z_k=\frac{p_k-H_kq_k}{\alpha_k z_k^\top q_k} zk=αkzkqkpkHkqk,最终的一个迭代形式为: H k + 1 = H k + ( p k ) − H k q k ) ( p k + H k q k ) ⊤ q k ⊤ ( p k − H k q k ) H_{k+1}=H_k+\frac{(p_k)-H_kq_k)(p_k+H_kq_k)^\top}{q_k^\top(p_k-H_kq_k)} Hk+1=Hk+qk(pkHkqk)(pk)Hkqk)(pk+Hkqk),这样就用一个迭代形式可以不断地计算得到近似矩阵 H k H_k Hk,用于替换牛顿法中海森矩阵地逆。

20221030~20221031

  • 下午去操场碰到王炳杰在刷跑协的跑量活动,准备给他女票进个手环。我看他最近应该休息得还行,准备带他上上强度,4’15"带了三公里他就崩了,我是奔着万米去跑的,因为今天距离上一次10km已经过去整整7个月了,真的无法接受这个事实,但还是5km就崩,耐力真的好难完全恢复。
  • 我想想还是进跑量活动群里看了一下(这周太忙肯定刷不了跑量)。突然发现群主袁佩瑶(她是去年高百被忽悠去做志愿者的新生,1米7+,排球队成员)跟陈嘉伟的头像是情头,我后知后觉,原来如此!这是谈了已经老长时间了啊,就很搞,去年在车上嘉伟还说不可能喜欢这种类型的女生(指巨话痨),我当时就觉得俩人都是新生,又是甘肃老乡,又都喜欢跑步,凑一对差不多得了,现在想想去年运动会袁佩瑶还来给嘉伟加油,果然任何人逃不过真香定律。

转博考备忘:

  • X ∼ N ( θ , σ 2 ) , θ ∈ N ( μ , τ 2 ) , σ 2 , μ , τ 2 X\sim N(\theta,\sigma^2),\theta\in N(\mu,\tau^2),\sigma^2,\mu,\tau^2 XN(θ,σ2),θN(μ,τ2),σ2,μ,τ2已知,则 θ ∣ X ˉ ∼ N ( n τ 2 X ˉ + σ 2 μ n τ 2 + σ 2 , σ 2 τ 2 n τ 2 + σ 2 ) \theta|\bar X\sim N\left(\frac{n\tau^2\bar X+\sigma^2\mu}{n\tau^2+\sigma^2},\frac{\sigma^2\tau^2}{n\tau^2+\sigma^2}\right) θXˉN(nτ2+σ2nτ2Xˉ+σ2μ,nτ2+σ2σ2τ2)

  • 关于内点法,以线性规划 min ⁡ c ⊤ x , A x = b , x ≥ 0 \min c^\top x, Ax=b,x\ge0 mincx,Ax=b,x0与对偶 max ⁡ b ⊤ p , A ⊤ p + s = c , s ≥ 0 \max b^\top p,A^\top p+s=c,s\ge0 maxbp,Ap+s=c,s0为例,首先转为barrier问题 min ⁡ c ⊤ x − μ ∑ i = 1 n log ⁡ x i , s.t.  A x = b , x > 0 \min c^\top x-\mu\sum_{i=1}^n\log x_i,\text{s.t. }Ax=b,x>0 mincxμi=1nlogxi,s.t. Ax=b,x>0,这个的kkt条件是 A x = b , x > 0 , A ⊤ p + s = c , 1 μ X S e − e = 0 , s > 0 Ax=b,x>0,A^\top p+s=c,\frac1\mu XSe-e=0,s>0 Ax=b,x>0,Ap+s=c,μ1XSee=0,s>0

    于是,在给定当前非边界点 ( x ˉ , p ˉ , s ˉ ) (\bar x,\bar p,\bar s) (xˉ,pˉ,sˉ)的情况下,即 A x ˉ = b , x ˉ > 0 , A ⊤ p ˉ + s ˉ = c , s ˉ > 0 A\bar x=b,\bar x>0,A^\top \bar p+\bar s=c,\bar s>0 Axˉ=b,xˉ>0,Apˉ+sˉ=c,sˉ>0时,我们要找一个步长 ( Δ x , Δ p , Δ s ) (\Delta x,\Delta p,\Delta s) (Δx,Δp,Δs)到达下一个迭代点,这需要满足 A Δ x = 0 , A ⊤ Δ p + Δ s = 0 , S ˉ Δ x + X ˉ Δ s = μ e − X ˉ S ˉ e − Δ x Δ s e A\Delta x=0,A^\top \Delta p+\Delta s=0,\bar S\Delta x+\bar X\Delta s=\mu e-\bar X\bar S e-\Delta x\Delta s e AΔx=0,AΔp+Δs=0,SˉΔx+XˉΔs=μeXˉSˉeΔxΔse,这里最后一个约束的最后一项 Δ x Δ s e \Delta x\Delta s e ΔxΔse是二阶无穷小项,可以省略,因此就是求解三个约束即可。

    实际迭代是这样的,从 x 0 , p 0 , s 0 x_0,p_0,s_0 x0,p0,s0开始,初始化还要选一个 μ 0 \mu_0 μ0,然后每次迭代时都要更新 μ 0 : = k μ 0 \mu_0:=k\mu_0 μ0:=kμ0 0 < k < 1 0<k<1 0<k<1是一个预设好的缩小参数,算法迭代直到 μ \mu μ非常小,此时barrier问题就收敛到原问题了。

  • ADMM算法,只用于解决 min ⁡ x , z f ( x ) + g ( z ) , A x + B z = b \min_{x,z}f(x)+g(z),Ax+Bz=b minx,zf(x)+g(z),Ax+Bz=b这种问题, f , g f,g f,g都是凸函数。方法是先写出拉格朗日函数 L ( x , z , y ) = f ( x ) + g ( z ) + y ⊤ ( A x + B z − b ) L(x,z,y)=f(x)+g(z)+y^\top(Ax+Bz-b) L(x,z,y)=f(x)+g(z)+y(Ax+Bzb),为了保证凸性,需要在加一个正则项 L ρ ( x , z , y ) = L ( x , z , y ) + ρ 2 ∥ A x + B z − b ∥ 2 2 L_\rho(x,z,y)=L(x,z,y)+\frac\rho2\|Ax+Bz-b\|_2^2 Lρ(x,z,y)=L(x,z,y)+2ρAx+Bzb22,接下来就是迭代:
    ( x k + 1 , z k + 1 ) = arg min ⁡ x , z L ρ ( x , z , y k ) y k + 1 = y k + ρ ( A x k + 1 + B z k + 1 − b ) \begin{aligned} &(x^{k+1},z^{k+1})=\argmin_{x,z}L_{\rho}(x,z,y^{k})\\ &y^{k+1}=y^k+\rho(Ax^{k+1}+Bz^{k+1}-b) \end{aligned} (xk+1,zk+1)=x,zargminLρ(x,z,yk)yk+1=yk+ρ(Axk+1+Bzk+1b)

    为了便于求解 arg min ⁡ \argmin argmin,会把第一个式子分两步走:
    x k + 1 = arg min ⁡ x L ρ ( x , z k , y k ) z k + 1 = arg min ⁡ z L ρ ( x k + 1 , z , y k ) y k + 1 = y k + ρ ( A x k + 1 + B z k + 1 − b ) \begin{aligned} &x^{k+1}=\argmin_{x}L_{\rho}(x,z^k,y^{k})\\ &z^{k+1}=\argmin_{z}L_\rho(x^{k+1},z,y^{k})\\ &y^{k+1}=y^k+\rho(Ax^{k+1}+Bz^{k+1}-b) \end{aligned} xk+1=xargminLρ(x,zk,yk)zk+1=zargminLρ(xk+1,z,yk)yk+1=yk+ρ(Axk+1+Bzk+1b)
    这里一般会用 w k = y k / β w^k=y^k/\beta wk=yk/β做一个代换,使得形式更加简洁。


20221101~20221102

  • 高级统计考完,感觉还行,反正都会写,也都写完了。忘带水,又不想提前交卷,就硬忍着干渴一早上,难受得要死。
  • 嘉伟上周日高百跑出39’45"的场地万米,震惊!这是我身边看到的第二个万米跑进40分钟的人,他去年高百才跑了44’06",属实给我羡慕死,我这辈子可能都很难跑出这个成绩。
  • 午睡了半小时起来就开始回忆早上的题目,三门全考完肯定记不得了,趁热打铁,全都做完印象相对深刻一点,留给下一届的备用。

2022 高级统计学 学科综合考试试题回忆

Problem 1

已知随机样本 { X 1 , X 2 , . . . , X n , . . . } \{X_1,X_2,...,X_n,...\} {X1,X2,...,Xn,...}是独立同分布采样自期望与方差分别为 μ , σ 2 \mu,\sigma^2 μ,σ2的全体,随机变量 N N N服从参数为 ( n , p ) (n,p) (n,p)的二项分布,定义随机变量 S S S
S = { ∑ i = 1 N X i if  N > 0 0 if  N = 0 S=\left\{\begin{aligned} &\sum_{i=1}^NX_i&\text{if }N>0\\ &0&\text{if }N=0 \end{aligned}\right. S= i=1NXi0if N>0if N=0
试计算随机变量 S S S的期望与方差。


Problem 2

已知二元随机变量 X , Y X,Y X,Y的联合概率密度函数为:
f X , Y ( x , y ) = { x e − x ( y + 1 ) if  x > 0 , y > 0 0 otherwise f_{X,Y}(x,y)=\left\{\begin{aligned} &xe^{-x(y+1)}&&\text{if }x>0,y>0\\ &0&&\text{otherwise} \end{aligned}\right. fX,Y(x,y)={xex(y+1)0if x>0,y>0otherwise

  • ( 1 ) (1) (1) 试求解 X , Y X,Y X,Y的边际概率密度函数 f X ( x ) , f Y ( y ) f_X(x),f_Y(y) fX(x),fY(y)
  • ( 2 ) (2) (2) 若给定 x 0 , y 0 x_0,y_0 x0,y0,试求解 E ( X ∣ Y = y 0 ) , E ( Y ∣ X = x 0 ) \mathbb E(X|Y=y_0),\mathbb{E}(Y|X=x_0) E(XY=y0),E(YX=x0)
  • ( 3 ) (3) (3) 定义事件 A \mathcal{A} A表示随机变量 X , Y X,Y X,Y中至少有一个数值不小于 1 1 1,试求解事件 A \mathcal{A} A发生的概率?

Problem 3

已知随机变量 X ∼ Exp ( 1 / λ ) X\sim \text{Exp}(1/\lambda) XExp(1/λ),即 X X X的概率密度函数为:
f X ( x ) = λ e − λ x ( x > 0 ) f_X(x)=\lambda e^{-\lambda x}\quad (x>0) fX(x)=λeλx(x>0)
定义随机变量 Y = ⌊ X ⌋ Y=\lfloor X\rfloor Y=X(即向下取整),定义随机变量 W = X − Y W=X-Y W=XY

  • ( 1 ) (1) (1) 给定非负整数 k k k以及 w ∈ ( 0 , 1 ) w\in(0,1) w(0,1),试求解 Pr ⁡ ( Y = k , W ≤ w ) \Pr(Y=k,W\le w) Pr(Y=k,Ww)
  • ( 2 ) (2) (2) 试求解 Cov ( Y , W ) \text{Cov}(Y,W) Cov(Y,W)

Problem 4

已知随机样本 X i ∼ i i d Exp ( 1 / λ i ) , i = 1 , 2 , . . . , n X_i\overset{\rm iid}\sim\text{Exp}(1/\lambda_i),i=1,2,...,n XiiidExp(1/λi),i=1,2,...,n,定义:
S = ∑ i = 1 n λ i X i , X min ⁡ = min ⁡ { X i } i = 1 n S=\sum_{i=1}^n\lambda_iX_i,\quad X_{\min}=\min\{X_i\}_{i=1}^n S=i=1nλiXi,Xmin=min{Xi}i=1n

  • ( 1 ) (1) (1) S S S服从何种分布?
  • ( 2 ) (2) (2) X min ⁡ X_{\min} Xmin服从何种分布?
  • ( 3 ) (3) (3) 试求解 Pr ⁡ ( X 1 = X min ⁡ ) \Pr(X_1=X_{\min }) Pr(X1=Xmin)

Problem 5

已知标准正态分布随机变量 Z Z Z的矩母函数为:
M Z ( t ) = e t 2 / 2 M_Z(t)=e^{t^2/2} MZ(t)=et2/2

  • ( 1 ) (1) (1) 考虑多元联合正态分布 X = ( X 1 , . . . , X n ) ∼ N ( μ , Σ ) {\bf X}=(X_1,...,X_n)\sim \mathcal{N}({\bf \mu},{\bf \Sigma}) X=(X1,...,Xn)N(μ,Σ),其中 μ = ( μ 1 , . . . , μ n ) \mu=(\mu_1,...,\mu_n) μ=(μ1,...,μn) Σ \Sigma Σ为协方差矩阵,定义 X {\bf X} X的矩母函数为 M X ( t ) = E ( e t ⊤ X ) M_{\bf X}({\bf t})=\mathbb{E}(e^{{\bf t^\top }{\bf X}}) MX(t)=E(etX),其中 t = ( t 1 , . . . , t n ) {\bf t}=(t_1,...,t_n) t=(t1,...,tn),试求解 M X ( t ) M_{\bf X}({\bf t}) MX(t)的具体表达式?
  • ( 2 ) (2) (2) 已知若 M X ( t ) = M X 1 ( t 1 ) ⋅ M X 2 ( t 2 ) ⋅ . . . ⋅ M X n ( t n ) M_{\bf X}({\bf t})=M_{X_1}(t_1)\cdot M_{X_2}(t_2)\cdot...\cdot M_{X_n}(t_n) MX(t)=MX1(t1)MX2(t2)...MXn(tn)可以推导出 X 1 , . . . , X n X_1,...,X_n X1,...,Xn是相互独立的,试证明若多元联合正态分布 ( X 1 , . . . , X n ) (X_1,...,X_n) (X1,...,Xn)是不相关的,则 ( X 1 , . . . , X n ) (X_1,...,X_n) (X1,...,Xn)是相互独立的。

Problem 6

随机变量 X X X的概率密度函数为:
f X ( x ) = x 2 2 θ 3 e − x / θ ( x ≥ 0 ) f_X(x)=\frac{x^2}{2\theta^3}e^{-x/\theta}\quad (x\ge0) fX(x)=2θ3x2ex/θ(x0)

  • ( 1 ) (1) (1) 试求解参数 θ \theta θ的最大似然估计量 θ ^ M L E \hat \theta_{\rm MLE} θ^MLE,请问 θ ^ M L E \hat \theta_{\rm MLE} θ^MLE是无偏的吗?
  • ( 2 ) (2) (2) θ ^ M L E \hat \theta_{\rm MLE} θ^MLE是否达到 CR \text{CR} CR下界?

Problem 7

已知随机变量 X X X服从正态分布 N ( μ , σ 2 ) \mathcal{N}(\mu, \sigma^2) N(μ,σ2),其中 σ 2 \sigma^2 σ2已知

  • ( 1 ) (1) (1) 给定 a 0 , a 1 , a 2 , a 3 a_0,a_1,a_2,a_3 a0,a1,a2,a3,试求一个 g ( μ ) = a 3 μ 3 + a 2 μ 2 + a 1 μ + a 0 g(\mu)=a_3\mu^3+a_2\mu^2+a_1\mu+a_0 g(μ)=a3μ3+a2μ2+a1μ+a0 UMVUE \text{UMVUE} UMVUE(最小偏差无偏估计量)
  • ( 2 ) (2) (2) 假定用 b X ˉ b\bar X bXˉ来估计参数 μ \mu μ X ˉ \bar X Xˉ为样本均值),试求 b b b的值,使得估计量 b X ˉ b\bar X bXˉ MSE \text{MSE} MSE最小(可以用 μ , σ 2 \mu,\sigma^2 μ,σ2来表示 b b b

Problem 8

已知随机变量 X X X服从参数为 ( 6 , θ ) (6,\theta) (6,θ)的二项分布( X = X 1 + X 2 + . . . + X 6 X=X_1+X_2+...+X_6 X=X1+X2+...+X6),考虑假设检验:
H 0 : θ = 0.4 v.s. H 1 : θ > 0.4 H_0:\theta=0.4\quad\text{v.s.}\quad H_1: \theta>0.4 H0:θ=0.4v.s.H1:θ>0.4
假定拒绝域形如 R = { X > c } R=\{X>c\} R={X>c}

题干给出了 θ = 0.4 \theta=0.4 θ=0.4 θ = 0.7 \theta=0.7 θ=0.7的二项分布概率质量函数的表:

θ \theta θ 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6
0.4 0.4 0.4 0.0467 0.0467 0.0467 0.1866 0.1866 0.1866 0.3110 0.3110 0.3110 0.2765 0.2765 0.2765 0.1382 0.1382 0.1382 0.0369 0.0369 0.0369 0.0041 0.0041 0.0041
0.7 0.7 0.7 0.0007 0.0007 0.0007 0.0102 0.0102 0.0102 0.0595 0.0595 0.0595 0.1852 0.1852 0.1852 0.3241 0.3241 0.3241 0.3025 0.3025 0.3025 0.1176 0.1176 0.1176
  • ( 1 ) (1) (1) 显著水平为 0.01 0.01 0.01~ 0.05 0.05 0.05,试确定 c c c的取值。
  • ( 2 ) (2) (2) 根据 ( 1 ) (1) (1) c c c值确定得到的拒绝域,试计算 θ = 0.7 \theta=0.7 θ=0.7时的功效函数 β ( 0.7 ) \beta(0.7) β(0.7)
  • ( 3 ) (3) (3) 取样得到的结果为 ( 1 , 0 , 1 , 1 , 1 , 0 ) (1,0,1,1,1,0) (1,0,1,1,1,0),试求 P P P值,并说明此时在 0.05 0.05 0.05的显著水平下是否拒绝原假设?

Problem 9

已知随机变量 X 1 , X 2 , . . . , X n X_1,X_2,...,X_n X1,X2,...,Xn是独立同分布采样自下面概率密度函数的样本:
f X ( x ) = θ x θ − 1 ( 0 < x < 1 ) f_X(x)=\theta x^{\theta-1}\quad (0<x<1) fX(x)=θxθ1(0<x<1)

  • Y i = − log ⁡ X i Y_i=-\log X_i Yi=logXi的分布为?
  • 试说明 X 1 , . . . , X n X_1,...,X_n X1,...,Xn的似然函数关于统计量 T = X 1 X 2 . . . X n T=X_1X_2...X_n T=X1X2...Xn具有单调似然比
  • 写出 H 0 : θ < θ 0  v.s.  H 1 : θ ≥ θ 0 H_0:\theta<\theta_0\text{ v.s. }H_1:\theta\ge\theta_0 H0:θ<θ0 v.s. H1:θθ0水平为 α \alpha α UMP \text{UMP} UMP检验方法

20221103

  • 计量考吐了,srds,还是花了四小时把8页答题纸写得满满,反正我已经在能力范围内尽我所能了,能不能通过就看天了。
  • 看到hwt和lzw周六要做汇报,我想要不要去听一下,这两个人进度倒是挺快,这么快就已经出成果了嘛,搞得我压力有点大,考完得赶紧动笔开始写东西了。
  • 照例午睡后回忆了一下题目,有一题确实想不起来了。

2022 学科综合考试 高级计量经济学试题回忆

Problem 1

考虑多元线性回归 y i = β 0 + β 1 x 1 i + . . . + β k x k i + μ i y_i=\beta_0+\beta_1x_{1_i}+...+\beta_kx_{ki}+\mu_i yi=β0+β1x1i+...+βkxki+μi,存在异方差 σ 2 ( μ ∣ x ) \sigma^2(\mu|x) σ2(μx)

  • ( 1 ) (1) (1) 若已知存在异方差的形式为 σ 2 ( μ ∣ x ) = σ 2 h ( x ) \sigma^2(\mu|x)=\sigma^2h(x) σ2(μx)=σ2h(x),其中 h ( x ) = exp ⁡ ( δ 1 x 1 + . . . + δ k x k ) h(x)=\exp(\delta_1x_1+...+\delta_kx_k) h(x)=exp(δ1x1+...+δkxk),但是 σ 2 \sigma^2 σ2以及 δ 1 , . . . , δ k \delta_1,...,\delta_k δ1,...,δk未知,请写出FGLS(可行的广义最小二乘法)的消除异方差的过程
  • ( 2 ) (2) (2) 写出White异方差检验的过程。

Problem 2

考虑多元线性回归 y i = β 0 + β 1 x 1 i + . . . + β k x k i + μ i y_i=\beta_0+\beta_1x_{1_i}+...+\beta_kx_{ki}+\mu_i yi=β0+β1x1i+...+βkxki+μi

  • ( 1 ) (1) (1) Cov ( x 1 i , μ i ) ≠ 0 \text{Cov}(x_{1i},\mu_i)\neq 0 Cov(x1i,μi)=0,则所有估计参数都不具有一致性,这种说法正确吗?为什么?
  • ( 2 ) (2) (2) 请问可以用 Q = ∑ i = 1 n ( y ^ i − y i ) 2 n Q=\sum_{i=1}^n(\hat y_i-y_i)^{2n} Q=i=1n(y^iyi)2n n n n为自然数,来作为目标函数估计参数吗?
  • ( 3 ) (3) (3) R ˉ 2 = 1 − ( R S S / ( n − k − 1 ) ) / ( T S S / ( n − 1 ) ) \bar R^2=1-(RSS/(n-k-1))/(TSS/(n-1)) Rˉ2=1(RSS/(nk1))/(TSS/(n1)),请问 R ˉ 2 \bar R^2 Rˉ2可能是负数吗?

Problem 3

考虑两个回归( y ˉ , x ˉ 1 , x ˉ 2 \bar y,\bar x_1,\bar x_2 yˉ,xˉ1,xˉ2表示样本对应的数据列的平均值):
y i = β 0 + β 1 x 1 i + β 2 x 2 i + μ i y − y ˉ = θ 1 ( x 1 i − x ˉ 1 ) + θ 2 ( x 2 i − x ˉ 2 ) + v i i = 1 , 2 , . . . , n y_i=\beta_0+\beta_1x_{1i}+\beta_2x_{2i}+\mu_i\\ y-\bar y=\theta_1(x_{1i}-\bar x_1)+\theta_2(x_{2i}-\bar x_2)+v_i\\ i=1,2,...,n yi=β0+β1x1i+β2x2i+μiyyˉ=θ1(x1ixˉ1)+θ2(x2ixˉ2)+vii=1,2,...,n

  • ( 1 ) (1) (1) 写出两个回归的正规方程组
  • ( 2 ) (2) (2) 写出 θ 1 \theta_1 θ1的估计量
  • ( 3 ) (3) (3) θ 1 \theta_1 θ1的估计量是否等于 β 1 \beta_1 β1的估计量?为什么?

Problem 4

100个班级的平均分Testscore与班级人数CN构建一元线性回归模型得到方程 T e s t s c o r e = 520.4 − 2.82 ∗ C N Testscore=520.4-2.82*CN Testscore=520.42.82CN,拟合优度 R 2 = 0.08 R^2=0.08 R2=0.08(就是这么低), σ 2 = 11.5 \sigma^2=11.5 σ2=11.5(没有说这个 σ 2 \sigma^2 σ2是什么意思)

  • ( 1 ) (1) (1) 20个人班级,回归预测的平均分是多少?
  • ( 2 ) (2) (2) 班级人数从19变为23,回归预测的平均分变化多少?
  • ( 3 ) (3) (3) 100个班级的平均人数为24,则100个班级的平均分是多少?
  • ( 4 ) (4) (4) 100个班级平均分的标准差是多少?(这个应该是要用拟合优度来算的)

Problem 5

数据生成过程 x t = ρ x t − 1 + ϵ t x_t=\rho x_{t-1}+\epsilon_t xt=ρxt1+ϵt ϵ t \epsilon_t ϵt是白噪声( t = 1 , 2 , . . . , t=1,2,..., t=1,2,...,

  • ( 1 ) (1) (1) ρ = − 1 \rho=-1 ρ=1,给定 T T T,求 x T x_T xT的期望与方差?
  • ( 2 ) (2) (2) ρ = − 1 \rho=-1 ρ=1,序列平稳吗?为什么
  • ( 3 ) (3) (3) ρ = − 1 \rho=-1 ρ=1,求解自相关函数 ρ ( x T , x T + 1 ) \rho(x_{T},x_{T+1}) ρ(xT,xT+1) ρ ( x T , x T + 2 ) \rho(x_{T},x_{T+2}) ρ(xT,xT+2)
  • ( 4 ) (4) (4) ρ = 1 \rho=1 ρ=1,求解自相关函数 ρ ( x T , x T + 1 ) \rho(x_{T},x_{T+1}) ρ(xT,xT+1) ρ ( x T , x T + 2 ) \rho(x_{T},x_{T+2}) ρ(xT,xT+2)
  • ( 5 ) (5) (5) 根据 ( 3 ) ( 4 ) (3)(4) (3)(4),说明 ρ = 1 \rho=1 ρ=1 ρ = − 1 \rho=-1 ρ=1两个序列的关联与差异

Problem 6

数据生成过程: x t = β 0 + β 1 x t − 1 + β 2 x t − 2 + ϵ t x_t=\beta_0+\beta_1x_{t-1}+\beta_2 x_{t-2}+\epsilon_t xt=β0+β1xt1+β2xt2+ϵt ϵ t \epsilon_t ϵt是白噪声,且 Var ( ϵ t ) = 1 \text{Var}(\epsilon_t)=1 Var(ϵt)=1

  • β 0 = 0 , β 1 = 0.1 , β 2 = 0.3 \beta_0=0,\beta_1=0.1,\beta_2=0.3 β0=0,β1=0.1,β2=0.3,是否平稳?若是,则平稳时 x t x_t xt的期望与方差是多少?
  • β 0 = 0.2 , β 1 = 0.6 , β 2 = 0.7 \beta_0=0.2,\beta_1=0.6,\beta_2=0.7 β0=0.2,β1=0.6,β2=0.7,是否平稳?若是,则平稳时 x t x_t xt的期望与方差是多少?

Problem 7

忘了

Problem 8

x t = x t − 1 + u t , y t = y t − 1 + v t x_t=x_{t-1}+u_t,y_t=y_{t-1}+v_t xt=xt1+ut,yt=yt1+vt u t , v t u_t,v_t ut,vt是平稳序列

  • ( 1 ) (1) (1) 若构建回归 y t = a 0 + a 1 x t + ϵ t y_t=a_0+a_1x_t+\epsilon_t yt=a0+a1xt+ϵt,这是虚假回归(或伪回归)吗?
  • ( 2 ) (2) (2) x t x_t xt有两个单位根, y t y_t yt有三个单位根,写出 x t x_t xt y t y_t yt的数据生成过程
  • ( 3 ) (3) (3) 假定 x t x_t xt y t y_t yt分别表示GDP与财政收入,有 ln ⁡ y t = α 0 + α 1 ln ⁡ y t − 1 + α 2 ln ⁡ y t − 2 + β 1 ln ⁡ x t − 1 + β 2 ln ⁡ x t − 2 \ln y_t=\alpha_0+\alpha_1\ln y_{t-1}+\alpha_2\ln y_{t-2}+\beta_1\ln x_{t-1}+\beta_2 \ln x_{t-2} lnyt=α0+α1lnyt1+α2lnyt2+β1lnxt1+β2lnxt2
    • ( a ) (a) (a) 请问系数 α 1 , α 2 , β 1 , β 2 \alpha_1,\alpha_2,\beta_1,\beta_2 α1,α2,β1,β2的经济学含义
    • ( b ) (b) (b) 模型趋于平稳后,如果在第 T T T年,发生突然冲击, ln ⁡ x T \ln x_{T} lnxT增加了一个单位,说明在接下来五年( T + 1 , T + 2 , T + 3 , T + 4 , T + 5 T+1,T+2,T+3,T+4,T+5 T+1,T+2,T+3,T+4,T+5 ln ⁡ y t \ln y_t lnyt受到的影响。

Problem 9

有人为了探讨国民生产总值(GDP)与对外贸易总额(TRA)的关系,构建模型 ln ⁡ G D P i = α 0 + α 1 T R A i + μ i \ln GDP_i=\alpha_0+\alpha_1TRA_i+\mu_i lnGDPi=α0+α1TRAi+μi T R A i = β 0 + β 1 ln ⁡ G D P i + v i TRA_i=\beta_0+\beta_1\ln GDP_i+v_i TRAi=β0+β1lnGDPi+vi,估计得到的参数为 α ^ 1 = 2.93 , β ^ 1 = 10.18 \hat\alpha_1=2.93,\hat \beta_1=10.18 α^1=2.93,β^1=10.18,因此说 T R A TRA TRA G D P GDP GDP的弹性影响系数为 2.93 % 2.93\% 2.93% G D P GDP GDP T R A TRA TRA的弹性影响系数为 10.18 % 10.18\% 10.18%,该模型与分析有哪些问题?

Problem 10

如何使用计量经济学模型对以下问题建模分析?

  • ( 1 ) (1) (1) 探讨1991年到2020年人民币汇率的变化及未来趋势
  • ( 2 ) (2) (2) 探讨1991年到2020年中国金融业发展对GDP的影响
  • ( 3 ) (3) (3) 探讨股票市场与实体经济的背离程度

20221104

  • 早上考完最后一门运筹,实话说运筹做得也不太顺手,虽然还是全部写完了,总归比计量有把握一些。真的是退化了,我看刘天浩三门也都硬刚到最后,虽然前几天提前交卷的人今天还是提前一个多小时就交卷,也不知道是真的胸有成竹还是觉得分数够及格也懒得再浪费时间。
  • 下午2km+400m*2,3’50"的配速跑的,跑到第七圈头开始疼,不想硬顶就回实验室了,可能是风大,而且中午不知为何心跳得特别快,根本睡不着,躺到两点就回学校干活了,明天训练再慢慢恢复。这学期过了两个月,感觉也没怎么偷懒,就是状态一直回不来,想想十月被拉去fc还是太伤,直接导致九月等于白练。

运筹的题我不想回忆了,因为我感觉运筹明显题型跟计量和统计不太一样,运筹更偏于即兴出题,而且都有具体数值,实在很难回忆出来具体的题干式子。复习了半天的内点法,牛顿、拟牛顿、梯度下降、ADMM、整数规划、MIP居然一个都没考。大致说一下每道题是什么东西

  • 第一题建模题20分应该问题不大,是个很简单的作业调度问题

  • 然后第二题五选二的常识题20分就懵了,居然五个都不是很有把握,虽然最后我把五题全写了,因为它是取分最高的两题。然后就特别慌

    • 如何判断迭代算法在难以解决的问题上迭代终止时解的质量?(我记得像梯度下降和牛顿法都有分析收敛速度以及解的approx程度的,但是我确实记不得具体结论是什么了)
    • 构造一个具有duality gap的原问题对偶问题,这个我是构造了一个不满足slater条件的,就是不存在可行的内点,所以就是 min ⁡ log ⁡ ( x + 1 ) , s.t.  x 2 ≤ 0 \min \log (x+1),\text{s.t. } x^2\le 0 minlog(x+1),s.t. x20,这个问题最优值显然是 0 0 0,它的对偶最优值是 − ∞ -\infty
    • 构造一个存在有界最优值,但不存在最优解的连续凸优化,这个我真的不确定,我写的是 min ⁡ 1 / x  s.t.  x ≥ 0 \min 1/x\text{ s.t. }x\ge 0 min1/x s.t. x0,但是1/x好像不能是凸函数…(虽然定义域是凸函数)
    • 证明凸函数 f ( x ) = log ⁡ ( ∑ i = 1 n exp ⁡ ( a i ⊤ x + b i ) ) f(x)=\log(\sum_{i=1}^n\exp(a_i^\top x+b_i)) f(x)=log(i=1nexp(aix+bi)))这个提示说用epigraph,但是我觉得这个怎么看都是应该用凸函数和仿射函数的复合来证啊, f ( x ) = g ( A x + b ) f(x)=g(Ax+b) f(x)=g(Ax+b),其中 g ( t ) = log ⁡ ( ∑ i = 1 n exp ⁡ ( t i ) ) g(t)=\log(\sum_{i=1}^n\exp(t_i)) g(t)=log(i=1nexp(ti)),也特别迷。
    • 最后一个是写最优性条件, max ⁡ x 2 + 3 y 2 + 2 x y \max x^2+3y^2+2xy maxx2+3y2+2xy,条件是 x + y ≤ 1 , x ≥ 0 , y ≥ 0 x+y\le 1,x\ge0, y\ge0 x+y1,x0,y0,最优解特别好找是 ( 0 , 1 ) (0,1) (0,1),但是题目说不要求解,只要给出最优性条件。我觉得我一定是昏了,我最后是把对偶写了一遍,然后给的 K K T \rm KKT KKT条件。
  • 第三题判断题5*2’,分值小,但是也触及了复习盲点,记两个不确定的:

    • 分式线性函数是拟凸函数?这个如果是双曲线一支肯定是拟凸(也是拟凹),但是我真的当时忘了拟凸的定义了,只记得是下水平集是凸集,但是那个小于两端最大值的定义给忘了。
    • 凸函数的卷积是凸函数?实话说凸函数的卷积这个名词听都没听过,反正保凸操作里没它,我就选错误了。
  • 第四题:两小题(2*15’),第一个小题是求解一个三元变量的线性规划,方法任意,我也不多事,单纯形法搞定;第二个小题给出的规划式是一个典型的运输问题,我觉得最优解是特别好找的,但是题目说是要写出这个规划符合哪种特殊形式(我只能写是运输问题了,因为我也不知道还能符合什么形式了),然后可用怎样的特殊方法求解,如果找不出这个特殊形式,最多只能给10分。这个的话因为系数给得特别简单,运输成本小的那个很明显是单调的,运输成本高的那个刚好是完全对称的,所以最优解用贪心法就可以找到,我也不知道有什么特殊方法,反正得出结果就拉倒了。

  • 第五题:两小题(2*10’),第一个小题是写一个锥线性规划的对偶,但是要求尽可能对偶形式简单,我好像并不能推到很简单的形式,应该只能算是写了个半成品。第二个是给了一个二次规划,然后给了一个可行解,判断是否为最优解,这个我也不是很确信,大致用KKT推了一下,把对偶问题的对应解找到了,我觉得是不符合KKT第三个条件,所以不是最优解。

最坏情况应该也能有70分,真的拉垮得离谱,丢人丢到家了。好歹这三门课当时还是总评95分左右,除了统计,其他两门过了一年多考得跟智障似的,反正四个小时耗尽,答题纸也写满了,已经是力所能及范围内做到最好了,这三门对我的研究方向帮助都很小(尤其是计量和运筹),学完就丢,菜得真实。


20221105

  • 下午例训,我铁了心要上一次万米,让嘉伟带我跑万米,可惜嘉伟带得太不稳,快到3’50",慢到4’20"多,过山车般干了5000米,实在是顶不住,最终是4’09"的均配,用时20’48"。老王虚得要死,跟了两三圈就掉队了,恋爱使人疲惫,恋爱使人面目全非,跑完跟老王谈了许久(其实就是在摸鱼),我一看就知道最近他累得不行,而且心事忡忡。他说他女票石家庄人,所以希望他之后能去北京发展,老王本来只想安安稳稳保个复旦管院(以他年级第一的绩点,去复旦肯定是十拿九稳),现在又在纠结如何保上清北。
  • 我还是建议他不要压力太大,顺其自然,处理好当下的事情往往比规划不确定的未来更重要。其实很容易理解,老王这种人对自己极其严苛,凡事预则立,都想做到尽可能完美,然独善其身尚不能及,更不要说要完美地规划两个人的未来。如果把结婚成家当作终点,未免显得过于超前与不切实际。条条大路通罗马,想要通向终点也并非只有一条路可走,总是要走一些弯路的,人生哪有那么理想。

CoMatching实现:A Co-Matching Model for Multi-choice Reading Comprehension

# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn

import torch

from torch.nn import Module, Linear, LSTM, functional as F

def CoMatching(Module):
	"""Reference: [A Co-Matching Model for Multi-choice Reading Comprehension](https://arxiv.org/abs/1806.04068)
	- Inputs: 
	  - $P \in \R^{p×d}$
	  - $Q \in \R^{q×d}$
	  - $A \in \R^{a×d}$
	  where $d$ is embedding size, $p, q, a$ are namely the sequence length of passage, question and answer.
	- Outputs:
	  - $C \in \R^{l×2l}$
	"""
	def __init__(self, args):
		super(CoMatching, self).__init__()
		
		self.p = args.max_article_sentence_token
		self.q = args.max_question_token
		self.a = args.max_option_token
		self.d = args.default_embedding_size
		self.l = args.comatching_bilstm_hidden_size
		
		print(f'p: {self.p}')
		print(f'q: {self.q}')
		print(f'a: {self.a}')
		print(f'd: {self.d}')
		print(f'l: {self.l}')
		
		self.Encoder_p = LSTM(input_size	= self.d,
							  hidden_size	= int(self.l / (1 + args.comatching_bilstm_bidirectional)),
							  num_layers	= args.comatching_bilstm_num_layers,
							  batch_first	= True,
							  bidirectional	= args.comatching_bilstm_bidirectional)
		self.Encoder_q = LSTM(input_size	= self.d,
							  hidden_size	= int(self.l / (1 + args.comatching_bilstm_bidirectional)),
							  num_layers	= args.comatching_bilstm_num_layers,
							  batch_first	= True,
							  bidirectional	= args.comatching_bilstm_bidirectional)
		self.Encoder_a = LSTM(input_size	= self.d,
							  hidden_size	= int(self.l / (1 + args.comatching_bilstm_bidirectional)),
							  num_layers	= args.comatching_bilstm_num_layers,
							  batch_first	= True,
							  bidirectional	= args.comatching_bilstm_bidirectional)

		self.W_g = Linear(self.l, self.l)
		self.W_m = Linear(2 * self.l, self.l)

	# @param p: (batch_size, max_article_sentence_token, embedding_size)
	# @param q: (batch_size, max_question_token, embedding_size)
	# @param a: (batch_size, max_option_token, embedding_size)
	def forward(self, P, Q, A):
		H_p, _ = self.Encoder_p(P)	# H_p: (batch_size, max_article_sentence_token, comatching_bilstm_hidden_size)
		H_q, _ = self.Encoder_q(Q)	# H_q: (batch_size, max_question_token, comatching_bilstm_hidden_size)
		H_a, _ = self.Encoder_a(A)	# H_a: (batch_size, max_option_token, comatching_bilstm_hidden_size)
		print(f'H_p: {H_p.shape}')
		print(f'H_q: {H_q.shape}')
		print(f'H_a: {H_a.shape}')

		H_p_T = H_p.permute(0, 2, 1)	# H_p: (batch_size, comatching_bilstm_hidden_size, max_article_sentence_token)
		H_q_T = H_q.permute(0, 2, 1)	# H_q: (batch_size, comatching_bilstm_hidden_size, max_question_token)
		H_a_T = H_a.permute(0, 2, 1)	# H_a: (batch_size, comatching_bilstm_hidden_size, max_option_token)
		print(f'H_p.T: {H_p.shape}')
		print(f'H_q.T: {H_q.shape}')
		print(f'H_a.T: {H_a.shape}')
		
		
		G_q = F.softmax(torch.bmm(self.W_g(H_q), H_p_T))	# G_q: (batch_size, max_question_token, max_article_sentence_token)
		G_a = F.softmax(torch.bmm(self.W_g(H_a), H_p_T))	# G_a: (batch_size, max_option_token, max_article_sentence_token)
		print(f'G_q: {G_q.shape}')
		print(f'G_a: {G_a.shape}')
		
		bar_H_q = torch.bmm(H_q_T, G_q)	# bar_H_q: (batch_size, comatching_bilstm_hidden_size, max_article_sentence_token)
		bar_H_a = torch.bmm(H_a_T, G_a)	# bar_H_a: (batch_size, comatching_bilstm_hidden_size, max_article_sentence_token)
		print(f'bar_H_q: {bar_H_q.shape}')
		print(f'bar_H_a: {bar_H_a.shape}')

		bar_H_q_T = bar_H_q.permute(0, 2, 1)	# bar_H_q_T: (batch_size, max_article_sentence_token, comatching_bilstm_hidden_size)
		bar_H_a_T = bar_H_a.permute(0, 2, 1)	# bar_H_a_T: (batch_size, max_article_sentence_token, comatching_bilstm_hidden_size)
		print(f'bar_H_q_T: {bar_H_q_T.shape}')
		print(f'bar_H_a_T: {bar_H_a_T.shape}')
		
		M_q = F.relu(self.W_m(torch.cat([bar_H_q_T - H_p, bar_H_q_T * H_p], axis=-1)))	# M_q: (batch_size, max_article_sentence_token, comatching_bilstm_hidden_size)
		M_a = F.relu(self.W_m(torch.cat([bar_H_a_T - H_p, bar_H_a_T * H_p], axis=-1)))	# M_a: (batch_size, max_article_sentence_token, comatching_bilstm_hidden_size)
		print(f'M_q: {M_q.shape}')
		print(f'M_a: {M_a.shape}')
		
		C = torch.cat([M_q, M_a], axis=-1)	# C: (batch_size, 2 * comatching_bilstm_hidden_size, max_article_sentence_token)

		return C


20221106~20221108

  • 这两天被push得很紧,有一种很无奈的屈服感。
  • 下午让嘉伟继续带10km,顺便试新到的飞飙361,肯定比飞燃PB好,但是鞋垫不是一体的,4km后右脚鞋垫居然跑飞出来了,后6km就导致右脚底磨得特别疼,整体4’08"的一个配速,实话说特别特别地吃力,如果不是中间休息了一次,肯定是跑不下来的,差不多算是持平10km个人最好成绩。

双向LSTM的另一个作用,因为序列一般会做padding,因此单向LSTM往往padding的位置靠后,对最终结果的影响更大,所以做双向后取平均是一个好的想法。

不过一般来说我们会做mask,即把padding的部分给mask掉,具体如下:

class MaskLSTM(Module):
	"""LSTM Module which deals with masked inputs"""
    def __init__(self,
				 input_size,
				 hidden_size,
				 num_layers,
				 batch_first,
				 bidirectional,
				 dropout):
        super(MaskLSTM, self).__init__()
        self.lstm = LSTM(input_size		= input_size,
						 hidden_size	= hidden_size,
						 num_layers		= num_layers,
						 batch_first	= batch_first,
						 bidirectional	= bidirectional,
						 dropout		= dropout)
        self.dropout = Dropout(dropout)

	# @param x			: (batch_size, sequence_length, feature_size)
	# @param x_shape	: (batch_size, )
    def forward(self, x, x_shape):
        x_mask = x.new(x.size()).zero_()
        for i in range(x_shape.size(0)):
            x_mask[i, :x_shape[i], :] = 1
        x_mask = Variable(x_mask, requires_grad=False)
		x_drop = self.drop_module(x * x_mask)
		x_hidden, _ = self.lstm_module(x_drop)
		x_hidden_mask = x_hidden.new(x_hidden.size()).zero_()
		for i in range(seq_lens.size(0)):
			x_hidden_mask[i, :x_shape[i], :] = 1
		x_hidden_mask = Variable(x_hidden_mask, requires_grad=False)
		return x_hidden * x_hidden_mask

20221109~20221111

  • 陈嘉伟真的是太可怕了,昨晚独自测试5000米,跑出18’50"的超级成绩,这要是有水平相当的一起跑,肯定会更快,我跟了7圈实在是跟不住了。这真的不对劲啊,我前几天才想起来问他跟袁佩瑶谈了多久了,没想到是从这学期才开始谈,你看王炳杰就很正常,才谈了两周就已经虚得不行,跑两步就要船,嘉伟怎么能边谈恋爱还能保持这么好的状态???想我挣扎到现在还不确定能否打开20分钟,真的是羞愧的无地自容。明天下午测专项,我准备要去试一试。
  • 晚上陪李乐康荧光夜跑,我说好只跑今晚,我是不会刷7天跑量的,去年被那个赵信淳骑自行车刷量超了第一,没拿到Salomon跑鞋,我就发誓再也不参加研会这种垃圾活动了。主要是李乐康来找我,我也不便拒绝,有机会跟高手上上强度也好。
  • 虽然李乐康在队里风评很差,但是我总觉得是可以包容的,想要找到一个完全三观相合的人很难很难,之前我看sxy的微博也觉得她太热衷女权了,有点可怕,但后来我发现可能也就那样,无非是发泄一下对社会不公的感慨,到头来该咋活还是咋活。在我们这种环境中,人心也不至于坏到哪儿去,只是各有不同的表达方式罢了,李乐康就是那种典型的不太会说话的人。退一步讲,人家狂也是有狂的资本,财大跑步水平很差咱们心理也知道,要是咱们队里有两三个陈嘉伟这种水平的,李乐康也不至于这么看不起田径队,尊重到底是靠实力赢来的,人家这种一看就是家境殷实出身,眼光高一些也情理之中。

记录一个小技巧:

众所周知,tensor.repeat的效果是这样的:

a2 = torch.Tensor([
	[7, 8, 9],
	[10, 11, 12],
])

a2.repeat(2, 1)
"""
tensor([[ 7.,  8.,  9.],
        [10., 11., 12.],
        [ 7.,  8.,  9.],
        [10., 11., 12.]])
"""

那么问题来了,有时候我想要得到

tensor([[ 7.,  8.,  9.],
        [ 7.,  8.,  9.],
        [10., 11., 12. ],
        [10., 11., 12.]])

可以这样写:

a2.repeat(1, 2).reshape(-1, a2.size(1))

这是二维的情况,如果是三维,比如a2的形状是(batchsize, seqlen, embedding_dim),我想要把batchsize中每个sequence都复制2份,即每个batchsize的sequence容量扩大两倍,而非变成2倍的batchsize,那么我们应该写成:

a2.repeat(1, 2, 1).reshape(-1, a2.size(1), a2.size(2))

注意不管是多少维的情况,repeat的2一定写在第二位上,然后reshape即可。

repeat很重要,可以在输入张量有很多重复的情况瞎,避免写循环,从而提升运行速度,但是可读性真的很差,而且有的时候repeat完经过计算还要还原成repeat前的情况,就很麻烦。


20221112

  • 下午给嘉伟当兔子,其实我也很想测5000米,但是我知道肯定跟不上嘉伟,所以就成人之美,帮嘉伟创造奇迹。
  • 第一个1000米3’17",我知道起手肯定特别快,所以就让嘉伟自己跑,但是没想到会这么快。我从第2个1000米上道给他破风,用时3’45";第三个1000米换炳杰3’46";第四个1000米继续换我带3’48";最后一个1000米前600米炳杰,后400米冲刺换我,3’40",最终5000米总用时18’16"!!! 嘉伟真乃神人也。人跟人的体质不能一概而论,而且嘉伟身体条件也特别好,精瘦干练,跟腱也长,天赋真的很重要。
  1. torch.nn.MaxPool1d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

  2. torch.nn.functional.avg_pool1d(input, kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True)

    用例:

    >>> # pool of square window of size=3, stride=2
    >>> input = Variable(torch.Tensor([[[1,2,3,4,5,6,7]]]))
    >>> F.avg_pool1d(input, kernel_size=3, stride=2)
    Variable containing:
    (0 ,.,.) =
      2  4  6
    [torch.FloatTensor of size 1x1x3]
    
  3. 这个就是窗口是123, 345,567

如果是二维,那么:

  1. 如果输入的大小是(N,C,H,W),那么输出的大小是(N,C,H_out,W_out)

  2. 举个例子:

    a = torch.FloatTensor([[[1,2,3,4], [2,3,4,5]]])# 1 2 4
    a_maxpool = F.max_pool2d(a, kernel_size =(1, 4) , stride=None)
    print(a_maxpool) # tensor([[[4.],[5.]]])
    # 可以执行a_maxpool.squeeze(-1)
    

    这个就是为了取每行的最大值。第一维N是batchsize

    上面这个例子等价于去做a_maxpool = F.max_pool1d(a, kernel_size =4 , stride=None),所谓1d,2d针对的是kernel,而非输入。


20221113~20221115

  • 下午自测5000米失败,八圈半熄火,均配3’59",是奔着破20分钟去跑的。前三个1000米配速分别为3’52",4’02",4’03",我也不知道为什么,明明过3000米时都不太吃力,但是一下子就坚持不下去,要是有个人带着在前面也不至于这么差劲。陈嘉伟开18分半之后,就有点特别焦虑,感觉做啥事儿都有点不如人了,下次等嘉伟来一定要让他带我再测一次。

这个是一个很值得去讨论的问题:

比如一个张量t1的形状是(2,3)

[
	[1,2,3],
	[2,3,4]
]

如果想要得到:

[
	[1,2,3],
	[1,2,3],
	[2,3,4],
	[2,3,4],
]

那么应该写t1.repeat(1, 2).view(-1, t1.size(1))

如果是简单写t1.repeat(2, 1),则得到的是:

[
	[1,2,3],
	[2,3,4],
	[1,2,3],
	[2,3,4],
]

如果是t1.repeat(2, 1),则得到的是:

[
	[1., 2., 3., 1., 2., 3.],
	[2., 3., 4., 2., 3., 4.]
]

那么这里就有一个问题了,如果我有两个张量 t 1 , t 2 t1, t2 t1,t2,形状分别是(batch_size, seqlen1, n_feature)(batch_size, seqlen2, n_feature),我想要把它们都变成(batch_size, seqlen1 * seqlen2, n_feature),且我希望在每个batch中,t[:, i*n+j, :]恰好对应第t1序列中的第i个元素以及t2序列中的第j个元素

比如下面的代码,这里batchsize=2,seqlen=2,n_feature=3

H_p = torch.FloatTensor([
	[
		[1,2,3],
		[2,3,4],
	],
	[
		[3,4,5],
		[4,5,6],
	]
])
print(H_p.repeat(1,2,1).view(H_p.size(0), -1, H_p.size(2)))
print(H_p.repeat(1,1,2).view(H_p.size(0), -1, H_p.size(2)))
tensor([[[1., 2., 3.],
         [2., 3., 4.],
         [1., 2., 3.],
         [2., 3., 4.]],

        [[3., 4., 5.],
         [4., 5., 6.],
         [3., 4., 5.],
         [4., 5., 6.]]])
tensor([[[1., 2., 3.],
         [1., 2., 3.],
         [2., 3., 4.],
         [2., 3., 4.]],

        [[3., 4., 5.],
         [3., 4., 5.],
         [4., 5., 6.],
         [4., 5., 6.]]])

其实H_p.repeat(1,2,1).view(H_p.size(0), -1, H_p.size(2))H_p.repeat(1,2,1)是完全一样的,即在哪个维度上进行扩充,就完全一样

一个非常简单记住repeat到底是如何改变张量的方法:

  • 假定 t t t的形状是 ( a 1 , a 2 , . . . , a n ) (a_1,a_2,...,a_n) (a1,a2,...,an),如果执行 t . r e p e a t ( b 1 , b 2 , . . . , b n ) t.repeat(b_1,b_2,...,b_n) t.repeat(b1,b2,...,bn),则相当于把 t t t变成 ( b 1 a 1 , b 2 a 2 , . . . , b n a n ) (b_1a_1,b_2a_2,...,b_na_n) (b1a1,b2a2,...,bnan),这个形状是可以reshape成 ( b 1 , a 1 , b 2 , a 2 , . . . , b n , a n ) (b_1,a_1,b_2,a_2,...,b_n,a_n) (b1,a1,b2,a2,...,bn,an),而不能是 ( a 1 , b 1 , a 2 , b 2 , . . . , a n , b n ) (a_1,b_1,a_2,b_2,...,a_n,b_n) (a1,b1,a2,b2,...,an,bn)的。其实更简单的策略是先做unsqueeze,然后在unsqueeze的维度上进行repeat,这样就不怕repeat之后的张量次序发生问题了。

20221116~20221117

  • 雨下了一天,于是在实验室从早八干到晚八,中午都没午睡。好久没这么认真学习过了,我实在不想卷到十点再走。走的时候发现雨差不多已经停了,小跑三圈放松一下。最近认识了新来到旁边的许兴鹏,感觉是一个很好相处的人,又算是半个老乡,讲道理死磕paper的时候有个伴到底动力足一些。
  • 这两天有点被刺激到,然而这样每天晚上出来跑的时候都会特别乏力。我不想特别用力地去push,然而越是这样,越是被生活push得很惨,前几天我其实想要更博客的,说起来已经好几个月没更了,这么长时间有好多东西可以更,但是写起来又嫌麻烦,想写得通俗易懂些还要费力去画图,索性还是算了。
  • 昨天跟队里练跳高跨栏的葛智杰吃饭,他现在大三眼下面临考研的压力,他不过也就比我小4岁,然而他这一代几乎就是只能选择考研一条路,不出国就没有别的选择,本科出来找工作确实寒碜了点,但是我还是觉得早点工作好,别瞎几把读多少书。之前硕士两个同学都选择了考公,其实真的跟SXY说的一样,现在真的有工作就行,没啥好挑的,活得太累,老得快,我都没力气跑步(上马看起来好像要办,血妈亏,我之前觉得上马必不可能办成,早知道还是报名碰个运气了,好想跑比赛阿!!!!!

CoMatching和DCMN都已经实现好并且跑通了,接下来把剩下两个DUMA和HRCA也尽快实现出来。复现别人的模型就会发现很多问题,主要可以归纳为两个问题:

  • 设计的算法中是否有存在重复计算?如果有,应当尽量避免。
  • 但是如果为了避免重复计算而不使用循环,大量使用repeat,reshape和permute操作,讲道理真的很容易写错算法逻辑。我在实现完CoMatching之后,特地又用循环写了一个易懂地版本,然后freeze了一下模型参数来确保两个版本得到地输出是一样的,发现前者就是很多地方写错了,改了好久才改对。

所以现在我倒是觉得那些paper里的更多复杂的模型,它的作者真的实现对了吗?主要是两个方面

  • 前馈传播运算是否符合paper中公式描述的那样?
  • 反向传播是否可求梯度

第二个问题真的困扰了我很久,比如有些端对端模型,前一个模型输出的是若干坐标(比如argmax函数输出的就是一种坐标),后一个模型直接拿坐标去进行运算,讲道理argmax是不能求梯度的,但是确实有一些近似算法能够尽可能逼近argmax的梯度(比如这一篇),有的说甚至就是用softmax来近似。

所以第一模型未必写对了,当然这个是可控的,只要你足够细心,但是要写到最优(即以尽可能少的运算次数实现同一个模型),这应该是很难的。

第二,模型未必可求最优,梯度都算不准,还想求最优解?讲道理求解器真的很重要,不过叉院他们现在研究的求解器也不可能拿来求解这种超级不规则的规划问题,到头来还是只能依赖算力得到尽可能好的解罢了。

于是很可能就是paper里写的是一个模型,实际上实现的是另一个模型,然后你说这个模型效果很好,其实你甚至也不知道这个模型长啥样。


20221118

  • 晚饭后计划上万米,虽然状态不是全盛,但是感觉勉强OK,4’10"的配速,结果刚过3000米,卢星雨突然冒出来,结果我的节奏被带弄乱了。我觉得出于礼貌应该慢一点下来等她,但是我又不想掉得太慢,因为前3000米节奏很好,感觉是可以坚持得下去的。就特别纠结,然后她跟了1000米实在是吃不消,搞得我忽快忽慢也累得要死。最后4’09"配速干了5000米就完了,又是上万米失败的一天。
  • 后来等王炳杰过来,我又带他跑了3000米,配速4’08",炳杰说他有点没有跑步的热情了,一个星期不跑已经掉到特别拉垮的水平了,每个人都有厌跑期,但是有个水平相当的同伴一起跑,确实要轻松和更有动力一些。
  • 最近雨下得很勤快,难得一个不怎么下雨的天,又没能尽兴,明后准备接着冲,这个月我无论如何都要上一次万米了,现在的瓶颈还是配速节奏,4’10"能跑完全程是最好,但是保险一点还是先缓到4’15"~4’20"这个区间上,主要这学期一直在练中长距离,速度压不下来,耐力也上不去。

写DUMA的时候,发现源码多头注意力其实里面还带了一个残差链接,而且不知道为什么多头注意力源码里,拼接完8个头之后,竟然少了一个全连接层,源码节选如下:

def multihead_attention(queries, keys, values, key_masks,
                        num_heads=8, 
                        dropout_rate=0,
                        training=True,
                        causality=False,
                        scope="multihead_attention"):
    '''Applies multihead attention. See 3.2.2
    queries: A 3d tensor with shape of [N, T_q, d_model].
    keys: A 3d tensor with shape of [N, T_k, d_model].
    values: A 3d tensor with shape of [N, T_k, d_model].
    key_masks: A 2d tensor with shape of [N, key_seqlen]
    num_heads: An int. Number of heads.
    dropout_rate: A floating point number.
    training: Boolean. Controller of mechanism for dropout.
    causality: Boolean. If true, units that reference the future are masked.
    scope: Optional scope for `variable_scope`.
        
    Returns
      A 3d tensor with shape of (N, T_q, C)  
    '''
    d_model = queries.get_shape().as_list()[-1]
    with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
        # Linear projections
        Q = tf.layers.dense(queries, d_model, use_bias=True) # (N, T_q, d_model)
        K = tf.layers.dense(keys, d_model, use_bias=True) # (N, T_k, d_model)
        V = tf.layers.dense(values, d_model, use_bias=True) # (N, T_k, d_model)
        
        # Split and concat
        Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # (h*N, T_q, d_model/h)
        K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # (h*N, T_k, d_model/h)
        V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # (h*N, T_k, d_model/h)

        # Attention
        outputs = scaled_dot_product_attention(Q_, K_, V_, key_masks, causality, dropout_rate, training)

        # Restore shape
        outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2 ) # (N, T_q, d_model)
              
        # Residual connection
        outputs += queries
              
        # Normalize
        outputs = ln(outputs)
 
    return outputs

outputs就是8个头的拼接,但是它根本没有再做乘以 W O W^O WO的操作了,仅仅是搞了个残差链接(而且这个残差链接原论文里也没有提到,后面还多个batchnorm就不提了),所以我觉得这些审稿人也不可能有时间去看源码,以前说数据能造假,现在连模型TM都能造假了。

另外我现在觉得tensorflow确实还是有可取之处的,比如tensorflow里的variable_scope,torch里确实没有类似的功能,其实我还是不太理解这里面的具体的细节,但是感觉上用variable_scope构建图确实能节约一些算力,避免造成重复计算。不过tf版本太乱,实在是不想去用了,其实会一个差不多也够了,两者也算是触类旁通了,真要改写成tf也不算太麻烦,只要代码是自己写的,别是从哪儿抄过来的。


20221119~20221120

  • 总觉得sxy昨晚是想说些什么,看得出来也是被生活整得心力憔悴,找个人倾诉也是好的。其实本来想这么久不见要不今天约出来吃个饭啥的,可是搞完pre的东西实在困得要死,想想还是算了,要是之后她回学校有机会再说了。
  • 今天42分整完成场地万米,均配4’12"。时隔近八个月,终于完成了今年第二个万米,跑完整个人都神清气爽,感觉又年轻了两岁。可能真的太久不跑万米,已经忘了跑万米的节奏是什么样。之前一直都是1.5m的步幅跟160的步频,心率很快就飙升到185以上,今天改到170步频+1.4m的步幅,平均心率179,这也是我第一次全程用前脚掌跑法干完的万米,其实感觉还挺轻松,如果最后两圈不冲刺,上到30圈应该不成问题。
  • 其实昨天仍然在挣扎着想上万米,但是雨后湿度大,10圈脱水乏力,真的特别无奈,这学期回来断断续续练了这么久,到现在跑个10圈都费事,觉得自己或许真的老了,在这个场地上,自己曾配速4’23"不间断无补给地跑完半马,如今看来竟是如此虚幻,迫于各种压力,心灰意冷想要不就放弃跑步,全身心投入到正业中去。我也不知道自己还能跑多久,功败垂成,皆在取舍之间,以前总是自视甚高,现在承认自己天赋努力都不如人,但总还是希望至少能让跑步陪伴我一路走下去的。

最近发现这个GIN(图同形网络,Graph isomorphism network,提出论文),说得还挺玄乎,说是具有最高区分度的GNN:
h i ( l + 1 ) = f Θ ( ( 1 + ϵ ) h i l + a g g r e g a t e ( { h j l , j ∈ N ( i ) } ) ) h_i^{(l+1)} = f_\Theta \left((1 + \epsilon) h_i^{l} + \mathrm{aggregate}\left(\left\{h_j^{l}, j\in\mathcal{N}(i) \right\}\right)\right) hi(l+1)=fΘ((1+ϵ)hil+aggregate({hjl,jN(i)}))

公式其实看起来和GCN相比就是Dense和CNN的复刻版,而且就自己查阅这么多paper来看,真的很少有用GIN,因为它这个消息生成和消息聚合都是用的MLP(其实看下面的例子就是个Dense,apply_func用的是lin,就是一个线性层),参数量可想而知的大。

另外记一下GCN和Sage的一个区别,GCN就是先把近邻节点的隐层表示做平均(消息聚合),然后用线性层映射一下,再激活;SAGE的话特殊一些,会先把近邻节点进行消息聚合(做平均,maxpooling,甚至用一个LSTM,这里比较特殊的是LSTM,因为近邻节点集合是无序的,所以这个LSTM的输入是随机打乱顺序的,那么问题来了,此时SAGE不再满足置换等变异性,permutation equvariance),然后再跟当前节点的隐层表示拼接,再线性映射完激活。

公式上来看,其实就是SAGE比GCN多一个 W h v Wh_v Whv(如果是用mean作为消息聚合函数, v v v是当前节点),但是从代码实现上来看,GCN只能是mean聚合,SAGE可以指定aggregator_type参数,且取值范围是mean, gcn, pool, lstm,注意这里有一个gcn,所以SAGE里可以嵌套一个GCN,如果用mean,其实两者也差不了太多。


(字数溢出)END

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值