序言
《白马非马》
月晕梦觉三更外,辗侧不寐天欲白。
长街小巷人不在,闭户方晓余空斋。
淫行庸碌民风改,凛冬难去花未拆。
粉饰贪墨莫仇忾,时有灾异毋自哀。
——囚生
辗转多时我还是回到了这里继续写,希望这篇可以写得更久一些,各种意义上。
本来是决定死磕到底,因为很想亲眼看看上位者何时才愿意在这荒唐的局面下做出让步。可是忽然又觉得拿别人的过错来惩罚自己实非明智之举,尤其是伙食质量日益参差不齐,越发无法满足日常训练需求。如果大多数人都坚定地选择留下来,必然是会对决策产生冲击的,可惜事与愿违。
走得很匆忙,提前一天半买票,然后赶忙办好各项事宜。虽然很不愿意这么形容,但是更像是一场大逃亡。出站后就被贴上各种标签集中管控,在被站务人员吆喝着快点走的时候,俘虏即将进入集中营的既视感跃然心头,前面的老爷子死活不肯加快步伐,透过背影能看出骨子里的那股倔强。
好在走出封楼警戒线后如释重负,两个月的高压状态得到缓解。还认识一个同行回无锡的学妹,结伴走了一遭,有种跟活人打交道的新鲜感。也算不得太坏罢。
最后是一些罔论:
- 我一直认为要多了解少发声,即悉实而慎论,因为有一百个人赞同你,就会有一千个人中伤你,从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-07-26~2022-07-30
- 2022-07-31~2022-08-31(完)
2022-05-25
关于数组拼接的易忘易混点(以torch.tensor
类型为例,numpy.ndarray
以及tensorflow
类似),即区分torch.cat
,torch.stack
,torch.vstack
,torch.hstack
四个函数:
-
一维情况:
假定有2个形状为
torch.Size([3])
的数组a1
和a2
,则有如下结论:①
torch.cat([a1, a2], axis=0)
的形状为torch.Size([6])
,即是左右拼接,且axis
参数只能取0
,不能取别的值。
②torch.stack([a1, a2])
的形状为torch.Size([2, 3])
,即是上下拼接;
③torch.hstack([a1, a2]])
的形状为torch.Size([6])
,等价于torch.cat([a1, a2], axis=0)
;
④torch.vstack([a1, a2]])
的形状为torch.Size([2, 3])
,等价于torch.stack([a1, a2])
; -
二维情况:
假定有2个形状为
torch.Size([3, 4])
的数组a1
和a2
,则有如下结论:①
torch.cat([a1, a2], axis=0)
的形状为torch.Size([6, 4])
,直观上看是上下拼接;torch.cat([a1, a2], axis=1)
的形状为torch.Size([3, 8])
,直观上看是左右拼接;总结axis
取值决定结果的形状是在第几维上进行叠加;
②torch.stack([a1, a2])
的形状为torch.Size([2, 3, 4])
,即是上下堆叠;
③torch.hstack([a1, a2]])
的形状为torch.Size([3, 8])
,等价于torch.cat([a1, a2], axis=1)
;
④torch.vstack([a1, a2]])
的形状为torch.Size([6, 4])
,等价于torch.cat([a1, a2], axis=0)
; -
三维情况:
假定有2个形状为
torch.Size([3, 4, 5])
的数组a1
和a2
,则有如下结论:
①torch.cat([a1, a2])
的规律与二维情况相同,即取决于axis
的取值将会得到torch.Size(6, 4, 5)
,torch.Size(3, 8, 5)
,torch.Size(3, 4, 10)
三种不同的结果;
②torch.stack([a1, a2])
的形状为torch.Size([2, 3, 4, 5])
,仍是上下堆叠;
③torch.hstack([a1, a2]])
的形状为torch.Size([3, 8, 5])
,等价于torch.cat([a1, a2], axis=1)
;
④torch.vstack([a1, a2]])
的形状为torch.Size([6, 4, 5])
,等价于torch.cat([a1, a2], axis=0)
; -
更高维情况的规律总结:
torch.cat
最好理解,根据axis
的取值决定最终形状(除axis
所在维外,其他维各个数组的形状必须相同),torch.hstack
与torch.vstack
总是分别对应torch.cat
取值axis=1
与axis=0
的情况,torch.stack
总是会使得结果的维数+1,即简单的将所有数组拼凑起来,但是必须要求所有数组的形状都相同。另外
numpy
的情况完全相同,其中torch.cat
替换为numpy.concatenate
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_translations[i].split()
# 变量初始化
nmt_ngram_counters = [] # 储存机器翻译序列的中所有的ngram短语, 并记录它们在机器翻译序列的中出现的次数
reference_ngram_counters = [] # 储存机器翻译序列的中所有的ngram短语, 并记录它们在机器翻译序列的中出现的次数
p_ns = [] # 储存所有p_n的取值
length_nmt_translation = len(nmt_translation) # 机器翻译序列的长度len(c)
# 计算len(r)
length_reference_translation_min = float('inf')
flag = float('inf')
for reference_translation in reference_translations:
length_reference_translation = len(reference_translation)
error = abs(length_reference_translation - length_nmt_translation)
if error <= flag and length_reference_translation <= length_reference_translation_min:
length_reference_translation_min = length_reference_translation
flag = error
# 统计机器翻译序列中的ngram频次
for n in range(k):
ngrams = []
for i in range(length_nmt_translation - n):
ngrams.append(' '.join(nmt_translation[i:i + n + 1]))
nmt_ngram_counters.append(dict(Counter(ngrams)))
# print(nmt_ngram_counters)
# print('-' * 64)
# 统计标准翻译序列中的ngram频次
for reference_translation in reference_translations:
reference_ngram_counters.append([])
for n in range(k):
ngrams = []
for i in range(len(reference_translation) - n):
ngrams.append(' '.join(reference_translation[i:i + n + 1]))
reference_ngram_counters[-1].append(dict(Counter(ngrams)))
# print(reference_ngram_counters)
# print('-' * 64)
# 计算p_n
for n in range(k):
p_n_numerator = 0 # p_n的分子部分
p_n_denominator = 0 # p_n的分母部分
for ngram in nmt_ngram_counters[n]:
p_n_numerator += min([max([reference_ngram_counters[i][n].get(ngram, 0) for i in range(len(reference_ngram_counters))]), nmt_ngram_counters[n][ngram]])
p_n_denominator += nmt_ngram_counters[n][ngram]
p_n = p_n_numerator / p_n_denominator
p_ns.append(p_n)
# 计算BP
if length_nmt_translation > length_reference_translation_min:
bp = 1
else:
bp = numpy.exp(1 - length_reference_translation_min / length_nmt_translation)
# 计算BLEU
bleu = bp * numpy.exp(sum([lambda_ * numpy.log(p_n) for lambda_, p_n in zip(lambdas, p_ns)]))
return bleu
reference_translations = [
'the light shines in the darkness and the darkness has not overcome it',
'and the light shines in the darkness and the darkness did not comprehend it',
]
nmt_translations = [
'and the light shines in the darkness and the darkness can not comprehend',
'the light shines the darkness has not in the darkness and the trials',
]
for nmt_translation in nmt_translations:
bleu = calc_bleu(nmt_translation=nmt_translation,
reference_translations=reference_translations,
lambdas=[.5, .5, .0, .0],
k=4)
print(bleu)
2022-05-31
- cs224n-winter-2022前10讲肝完(用时5天,刚好每天2讲课件+1次作业),共计5次作业,所以分成5个博客发布(6月1日零点准时发布),后面9讲属于是前沿研究探讨,计划精读其中几篇跟目前工作相关的paper,随缘更新。这几天效率真是超高。
- 下午SXY问我为什么点赞。其实我的真实想法是觉得这两个月以来断断续续聊过六七次,每次我都是被发起聊天的一方,总觉得有点太冷漠,主要被关了那么久,也没有什么值得高兴的事情,想要聊的时候总觉得会影响别人工作,每次都是我不知道回复什么,然后就不告而终,所以就手贱点了上去,不过也的确很喜欢这种柔美的旋律。
- 说起来再有一年很多人将毕业,一旦步入社会,关系大约就会像石沉大海一样飘散,虽然我也并没有在期望什么,只是这种偶尔的联系很像是小确幸,能给平凡的生活多增添些乐趣。或许真到失去的那天,才会因为没有珍惜而追悔莫及。
关于torch
的三个小功能:
tensor1 @ tensor2
等价于torch.matmul(tensor1, tensor2)
,矩阵乘法;masked_fill
的用法:给张量添加蒙布(防止读取未来信息,或是划分训练集验证集),一般是用-float('inf')
来进行填补。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]])
regist_buffer
:用于在模型中定义不想被收录到model_state_dict
的成员变量,同时可以节约内存:import torch.nn as nn import torch class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.register_buffer('a', torch.ones(2,3)) # 相当于是self.a, 但是不会记录到模型状态中, 可以进行修改, 用法与self.a完全一致 def forward(self, x): return x + self.a
2022-06-01
- 继续肝lecture11-19,后面的内容比较新也比较散,准备一起整完发布一篇博客收尾。接下来接着肝CS224W直到隔离结束,肚子里有货做正事才不虚。
- 昨天左大腿后侧有点小拉伤,我也不是很理解是怎么伤到的(回来一共走了40个负重箭步,就没有再练大腿后侧),600米就疼得受不了。补钙片今天跑步机35分钟8千米,右胸闷得厉害,心肺耐力肯定是掉了,慢慢练总会突破,下学期有运动会就跑5000米,弥补从小到大从未参加过运动会的遗憾。
- 看到高百6月资格赛开启,5月资格赛上海赛区一所上海高校都没有参赛,全是江浙皖鲁的高校,不知道后面还有没有机会,好想今年也能参赛。
整了个从ARXIV上抓论文的脚本,最近在分类整理看过的论文,这样效率要高很多,ARXIV破站半天登不上,爬虫倒是快得很:
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
import os
import re
import time
import json
import pandas
import requests
import warnings
from bs4 import BeautifulSoup
class ArxivCrawler:
def __init__(self):
self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
self.host = 'https://arxiv.org'
self.paper_api = 'https://arxiv.org/abs/{paper_id}'.format
self.pdf_api = 'https://arxiv.org/pdf/{paper_id}.pdf'.format
self.regexs = [
r'<[^>]+>'
]
self.compilers = [re.compile(regex, re.I) for regex in self.regexs]
def easy_download_pdf(self, paper_id, download_to='d:/'):
for paper_id in paper_ids:
response = requests.get(self.pdf_api(paper_id=paper_id), headers={'User-Agent': self.user_agent})
with open(os.path.join(download_to, f'{paper_id}.pdf'), 'wb') as f:
f.write(response.content)
def extract_paper_metadata(self, paper_id):
while True:
try:
response = requests.get(self.paper_api(paper_id=paper_id), headers={'User-Agent': self.user_agent})
break
except:
print(f'Having error get to {self.paper_api(paper_id)} ...')
time.sleep(60)
html = response.text
soup = BeautifulSoup(html, 'lxml')
# title
title_h1 = soup.find('h1', class_='title mathjax')
title = self.compilers[0].sub('', str(title_h1)).replace('\t', '').replace('\n', '').replace('Title:', '').strip()
# author
author_div = soup.find('div', class_='authors')
author = self.compilers[0].sub('', str(author_div)).replace('\t', '').replace('\n', '').replace('Authors:', '').strip()
# abstract
blockquote = soup.find('blockquote', class_='abstract mathjax')
abstract = self.compilers[0].sub('', str(blockquote)).replace('\t', '').replace('\n', '').replace('Abstract:', '').strip()
# bibtex
bibtex_div = soup.find()
if bibtex_div is None:
warnings.warn('DBLP not found!')
bibtex = None
else:
is_bibtex_existed = False
for link in bibtex_div.find_all('a'):
if str(link.string).replace(' ', '').replace('\n', '').replace('\r', '').replace('\t', '') == 'bibtex':
is_bibtex_existed = True
bibtex_url = link.attrs['href']
break
if not is_bibtex_existed:
warnings.warn('bibtex not found!')
bibtex = None
else:
while True:
try:
_response = requests.get(bibtex_url, headers={'User-Agent': self.user_agent})
break
except:
print(f'Having error get to {bibtex_url} ...')
time.sleep(60)
_html = _response.text
_soup = BeautifulSoup(_html, 'lxml')
bibtex_section_div = _soup.find('div', id='bibtex-section', class_='section')
if bibtex_section_div is None:
warnings.warn('bibtex section not found!')
bibtex = None
else:
pre = bibtex_section_div.find('pre')
if pre is None:
warnings.warn('bibtex pre not found!')
bibtex = None
else:
bibtex = str(pre.string)
metadata = {
'paper_id' : paper_id,
'url' : self.paper_api(paper_id=paper_id),
'title' : title,
'author' : author,
'abstract' : abstract,
'bibtex' : bibtex,
}
json.dump(metadata, open(f'{paper_id}.meta.json', 'w', encoding='utf8'))
return metadata
if __name__ == '__main__':
ac = ArxivCrawler()
paper_ids = [
'1707.07045'
]
for paper_id in paper_ids:
metadata = ac.extract_paper_metadata(paper_id=paper_id)
2022-06-02
- 盛夏将至,火药味儿也越来越重,最近的热搜无不充斥民粹,理想中的世界应当是包容万象,现实却到处在排斥异己,上至九鼎,下至草芥,无外乎此,争斗没有赢家。
- 理想终究只配给理性摁在地板上疯狂摩擦。
- CS224N完结,颇有所获,世事于我何加焉?
今天看TreeRNN时突然冒出很奇妙的想法,是否可以根据句法树结构来动态编辑编码器网络结构?瓶颈在于动态模型无法使用常规方法训练,因此想到可以用某种手法(如设计门控节点)来得到一个relax的模型(类比整数规划relax成线性规划),使得所有句法树对应的编码器结构可以统一到一种relax的模型架构中。感觉并不容易,但是一旦成功可能是目前极具创新意义的做法,之前的思路一直在想构建GNN来嵌入句法树,或许这种想法更合理。
我记得之前Google的AutoML也有提出这种自动学习神经网络架构的思路,怠惰如我到现在都没有好好看过AutoML的论文,希望跟它的想法是有所差异的。
2022-06-03
- 菜得扣脚,下午出去溜了一段,本想跑个长距离评测新买的佳明FR245,结果不到3km就熄火,配速只有4’26",客观讲33℃确实很热,心率很快就爆炸,但是掩盖不了整体拉胯,破三任重道远。要知道3月31日我还是跑出42分整的场地万米,结果刚恢复到巅峰就又坐了55天的牢,到头来又是从零开始,很不甘心却只能接受现实。
- 这几天心情不错,每天都能写些东西出来,老板也懒得管我,我想做事就做事,想写博客就写博客,想跑步就跑步,难得舒畅了一段时间。
- 但愿SXY也能尽快安顿好自己。人生总是如此,有低谷也有高岗,如果没有舍弃一切的勇气,那就应该坦然去面对所发生的一切。I wish you the best,端午安康。
关于Python正则库re的捕获元替换,常规的正则写法是在正则表达式中用小括号框出捕获元,然后在替换时使用$1,$2
来替换对应的捕获元(这在大部分的正则编译场景下都是通用的),结果老半天行不通,以为是正则编译的参数要改,尝试半天才发现是用\1,\2
来替换捕获元,下面是一个例子(将成对的圆括号替换为方括号):具体应用参考新发布的博客【端午安康SXY】Python正则表达式进阶用法(以批量修改Markdown英文字体为例)
import re
regex_compiler = re.compile(r'\(([^\)]+)\)') # 第一个'('是匹配字符'(', 第二个'('是捕获元左边界, 第一个')'是匹配非')'的字符, 第二个')'是捕获元右边界, 第三个')'是匹配字符')'
string = "正则表达式使用左圆括号('(')和右圆括号定位捕获元,如果要匹配左圆括号字符(如匹配':('表情),则需要在圆括号前添加反斜杠,右圆括号同理。"
help(regex_compiler.sub)
print(regex_compiler.sub(r'[\1]', string)) # 输出: 正则表达式使用左圆括号['(']和右圆括号框出捕获元,如果要匹配左圆括号字符[如匹配':('表情],则需要在圆括号前添加反斜杠,右圆括号同理。
2022-06-04
- 夜跑,雷阵雨,乱了节奏,然非常快乐。5km勉强23分钟,且不说巅峰期能跑进20分钟,去年差不多也是这个日子,骤雨中操场狂奔万米,只用不到45分钟,穿的还是很普通的运动鞋。
- 最近每天除了跑步外,都有花半个小时做负重深蹲、箭步划船、腹部和臀部的训练。然欲速不达,跑步这个事情很单纯,不跑就是零,其他不过是锦上添花,两个月的禁闭属实是给爷整麻了。
- 自己昨天一定是脑子抽风,又写一厢情愿的话。两年半,欲弃不舍,欲取不能。
这张图就作为一个耻辱柱,以此自励。看到WXY今天在健身房完成标铁,实在是不好意思发自己这么菜的记录
拆分一列(行)到多列(行)这种说法比较笼统,但是实际操作中又常会遇到,笔者记录几种常见的情形以供参考:
-
df.stack()
与df.unstack()
方法:返回值是一个pandas.Series
,长度为df
中所有元素的数量,前者是一行一行的依次遍历所有元素,后者是一列一列依次遍历所有元素。得到的pandas.Series
都包含两级索引,前者依次为行号与列名,后者依次为列名与行号。 -
使用
df.apply(func, axis=1, return_type='expand')
可以实现将一列扩展为多列:df = pandas.DataFrame({ 'array1': [[1, 2, 3], [4, 5, 6], [7, 8, 9]] }) print(df[['array1']].apply(lambda row: (row[0][0], row[0][1], row[0][2]), axis=1, result_type='expand'))
-
使用
df.apply(func, axis=0)
可以实现将一行扩展为多行:import pandas df = pandas.DataFrame({ 'color': ['blue,red,yellow', 'red,black', 'pink,grey'] }) print(df.apply(lambda column: column[0].split(',') + column[1].split(',') + column[2].split(','), axis=0))
-
对于字符串形式的字段,可以使用
df[column].str.split()
方法进行一列至多列的分割,如在上面的例子中使用:print(df['color'].str.split(',', expand=True))
-
多字段进行列划分时可能涉及表连接的操作,下面是一个具有代表性的例子:
df = pandas.DataFrame({ 'id' : ['212027', '212028'], 'color' : ['blue,red,yellow', 'red,black'], 'size' : ['XL,XXL,S', 'M,XL'], }) # 需要处理的数据表 split_columns = ['color', 'size'] # 需要分割的字段名称 df_processed = df.drop(columns=split_columns, axis=1) # 删除需要分割的字段 for column in split_columns: df_processed = df_processed.join(df[column].str.split(',', expand=True).stack().reset_index(level=1, drop=True).rename(column)) df_processed = df_processed.reset_index(drop=True) print(df_processed)
其中
df[column].str.split(',', expand=True).stack()
正如上文所述用于分割列后直接铺开,reset_index
的level
参数是指删除掉哪一级的索引,join
函数是根据索引进行表连接,与merge
需要区分。
2022-06-05
- 清晨特别可怕的梦,梦见SXY发消息说要结婚,图像竟然如此清晰,不到七点被惊醒,不寐,索性起来接着肝论文,后知后觉终会发生,又很真实。
- 晚饭前路跑间歇4.01km+2.64km,配速4’20",恢复到去年三月份的水平,慢慢在找回肌肉记忆,状态渐起,等隔离结束考虑申请个通行证进高中练场地。
- 这两天在精读CS224N里的一篇paper,争取今晚做完笔注。
PyTorch的masked_fill
方法记录(比如逐一解码的RNN解码器中每次解码都要对目标语言输入的未来分词信息进行MASK),通常用无穷大或负无穷大进行MASK
import torch
tensor= torch.randn(5, 6)
masked_ats = [5, 4, 3, 2, 1]
mask = torch.zeros(5, 6, dtype=torch.float)
for index, masked_at enumerate(masked_ats):
mask[index, masked_at:] = 1
print(mask)
tensor.data.masked_fill_(mask.byte(), -float('inf'))
print(tensor)
# ------------输出结果----------------
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]])
2022-06-06~2022-06-07
- 昨日跑休,跑步机维持状态,连日路跑膝盖渐渐不支,需要适当休息。
- 居家健康监测最后一天,沿湖夜跑,本还是奔着10km去,欲速不达,体能还没有恢复到能拉长距离,4’29"配5k多一些,差强人意。
- 打算明天剃个光头,算起来快4个月没去过理发店,中途自己胡剪两下,说实话感觉还不错,因为没留过长发,第一次发现自己的头发长长看着还可以,但是长发只会影响我跑步的速度,剪!
转道CS224W(图机器学习课程),感觉内容比较零散,花几天大致整理一下。
似乎GNN这块目前用PYG更流行一些,其实大致浏览了一下跟DGL半斤八两,学一个差不多就够了,触类旁通。
-
应该是要求PyTorch版本至少为
1.10.x
,可以去镜像站下载对应的PyTorch和Torchvision版本(20220607我更新torch1.7.0
到torch1.10.2
,torchvision0.8.3
到torchvision0.9.0
,一般来说最新版本都只有Linux系统使用的安装版本,选择次新的即可对应,当前时间点torch1.11.x
只有Linux版本); -
PYG的安装在文档中有给出,我用的方法是:
pip install torch-scatter torch-sparse torch-cluster torch-spline-conv torch-geometric -f https://data.pyg.org/whl/torch-1.10.0+cpu.html
下载速度非常快;
-
感觉PYG用法和DGL差不多,但是PYG应该是只支持PyTorch后端,DGL则可以支持PyTorch,Tensorflow,MXNet的三个用法;给一个示例demo:
import torch from torch.nn import Linear from torch_geometric.nn import GCNConv class GCN(torch.nn.Module): def __init__(self, input_dim=34, hidden_dim=4, embedding_dim=2, num_layers=2): super(GCN, self).__init__() torch.manual_seed(12345) self.classifier = Linear(embedding_dim, dataset.num_classes) self.convs = torch.nn.ModuleList() self.convs.append(GCNConv(input_dim, hidden_dim)) self.num_layers = num_layers for l in range(num_layers-1): self.convs.append(GCNConv(hidden_dim, hidden_dim)) self.conv3 = GCNConv(hidden_dim, embedding_dim) self.relu = torch.nn.ReLU() def forward(self, x, edge_index): h = x for l in range(self.num_layers): h = self.convs[l](h, edge_index) h = h.tanh() #h = self.relu(h) h = torch.nn.functional.relu(h) h = torch.nn.functional.dropout(h, p=0.5, training=self.training) h = self.conv3(h, edge_index) embeddings = h.tanh() # Final GNN embedding space. # Apply a final (linear) classifier. out = self.classifier(embeddings) return out, embeddings model = GCN() print(model)
2022-06-08
- 14天结束转绿码,出门理发,老妈坚持要带我去zxy(幼儿园同学)妈妈那边剪,我想去哪儿不是剪,反正都想好剃光头。理完回家路上老妈跟我说,本来想zxy去上海读研可以给你谈个对象,把你拎过来给未来的丈母娘看一看,可惜人家现在去天津了,人家长得还挺漂亮。我竟然也落魄到要相亲的地步,唉…
- 狂肝cs224w,运气好今天能结束。晚饭前3km,大逆风加湿热,难受(六月目前42.8km,基本合格)。
WIN11屏幕截图(指定区域)的功能好像没了,写了个截屏小脚本用于截论文的图片。
使用方法:运行程序等待2秒以上,然后分别点击需要截取的矩形区域的左上角和右下角,图片保存在D盘根目录下。
# -*- coding: UTF-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
import cv2
import time
import numpy
import logging
import pyautogui
from pynput.mouse import Controller
from pynput import mouse
def easy_show(image: numpy.ndarray, window_title: str="image") -> None:
"""
Show a image in a new window.
"""
cv2.imshow(window_title,image)
cv2.waitKey(0)
cv2.destroyAllWindows()
def mouse_listener():
def _on_move(x, y):
info = f'move to ({x}, {y})'
logging.info(info)
def _on_click(x, y, button, pressed):
info = f'{"click" if pressed else "released"} {button} at ({x}, {y})'
logging.info(info)
def _on_scroll(x, y, dx, dy):
info = f'scrolled at ({x}, {y}) by ({dx}, {dy})'
logging.info(info)
with mouse.Listener(on_move=_on_move, on_click=_on_click, on_scroll=_on_scroll) as listener:
listener.join()
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()
if __name__ == '__main__':
screenshot()
2022-06-09~2022-06-11
- 前天老妈医院里献血,萎靡好几天,讲道理抽掉十分之一的血量还挺可怕的,两天都没有精力。
- 昨晚外婆烧秸秆给大队部抓了,十点多老妈赶回老家去捞人,唉,花钱买个教训,外婆肯定心疼得要死。
- 状态有些低落。明天去扬州玩一天,权且放松,之前紧绷两周神经,放松一下。维持日均5k的跑量,慢慢找状态。
发现之前那个想法跟TreeRNN很像,还以为是自己第一个想到可以这么做的,到头来想出完全创新的点子还是挺难的,不过可以在前人基础上改进改进。在ARXIV上检索TreeRNN找到两篇:
后一篇是D.Manning的著作,应该是TreeRNN的提出文,值得关注。
关于NLTK里斯坦福的句法解析模块,最近报警告说即将被弃用,最新版将被nltk.parse.corenlp.StanforCoreNLPParser
模块取代,关于CoreNLP可以去斯坦福软件里下载JAR包,目前看至少依存分析和句法树是可行的,这两个也是最有用的,NER也能用,虽然分词和词性标注会报错,但是这两个也不必用非要用斯坦福的,有很多其他资源可以用,中文可以用jieba,英文的话nltk里就有内置的分词包和词性标注包,目前StanforCoreNLPParser
还没搞清楚具体用法,近期会发布关于如何使用斯坦福JAR包详细教程。
2022-06-12~2022-06-13
- 昨天一直玩到晚上九点半才回来,看了侏罗纪世界3,感觉比较面向青少年的,都没什么血腥场面,而且剧情比较庸俗,算上特效只能说是勉强合格。说起来过年之后就没再去过影院了。
- 现在都不能一口气完成5k,不过加起来到家也不过才15天,今年开学花了将近50天才完全恢复,还是要有耐心才行。
corenlp要升级nltk到最新版可用,调用的是远程接口,无需下载jar包到本地,但是容易连不上远程服务器。感觉是斯坦福不准备开放它们的解析包,而是封装成接口,看起来还挺fancy:
class CoreNLPParser(GenericCoreNLPParser)
| CoreNLPParser(url='http://localhost:9000', encoding='utf8', tagtype=None)
|
| >>> parser = CoreNLPParser(url='http://localhost:9000')
|
| >>> next(
| ... parser.raw_parse('The quick brown fox jumps over the lazy dog.')
| ... ).pretty_print() # doctest: +NORMALIZE_WHITESPACE
| ROOT
| |
| S
| _______________|__________________________
| | VP |
| | _________|___ |
| | | PP |
| | | ________|___ |
| NP | | NP |
| ____|__________ | | _______|____ |
| DT JJ JJ NN VBZ IN DT JJ NN .
| | | | | | | | | | |
| The quick brown fox jumps over the lazy dog .
2022-06-14~2022-06-15
- wyl又心血来潮,给我九天时间交出NFSC的申报书,难度很大。主要是照着我近期的工作展开,组里面前辈也都陆续毕业,有点青黄不接,所以只有我一个人写。CAIL的代码已经推了很长时间,我觉得也该做一些文字方面的工作,反正早晚论文里也要用到这些。
- 眼睛度数加深,去丹阳买眼镜。记得小时候丹阳眼镜都白菜价,现在四副也要一千多,时代真是变了。发现一种可以扣上镜腿固定住的,再也不用担心跑步时往下掉。目前月跑量过80km,虽然耐力依然拉垮,但是总量到位,而且日常的力量训练很有意义,到目前膝盖都没有明显疼痛。
- 这段时间不会很好过,酷暑难耐,训练陷入瓶颈,干爷爷又突然去世,加之申报书的DDL,本来还想要不要找个实习,想想还是专心做研究靠谱点了。
最近整理收藏夹发现一些比较冷僻的知识点,关于Python的异步操作:
import asyncio
from random import randint
async def do_stuff(ip, port):
print('About to open a connection to {ip}'.format(ip=ip))
reader, writer = await asyncio.open_connection(ip, port)
print('Connection open to {ip}'.format(ip=ip))
await asyncio.sleep(randint(0, 5))
writer.close()
print('Closed connection to {ip}'.format(ip=ip))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
work = [
asyncio.ensure_future(do_stuff('8.8.8.8', '53')),
asyncio.ensure_future(do_stuff('8.8.4.4', '53')),
]
loop.run_until_complete(asyncio.gather(*work))
使用async
定义异步函数,需要异步的部分使用await asyncio
来执行,其实没什么大用,但是在编写下载器逻辑,包括想自己编写磁力链接下载的代码就会很有意义,推荐一篇博客。
2022-06-16~2022-06-17
最近发现nltk.parser.StanfordDependencyParser
解析依存关系可能会没有结果:
def dependency_demo():
# 2022/06/10 13:21:17 通过测试
from nltk.parse.stanford import StanfordDependencyParser
eng_parser = StanfordDependencyParser(r'D:\data\stanford\software\stanford-parser-full-2020-11-17\stanford-parser.jar',
r'D:\data\stanford\software\stanford-parser-full-2020-11-17\stanford-parser-4.2.0-models.jar',
r'D:\data\stanford\software\stanford-parser-full-2020-11-17\englishPCFG.ser.gz')
res = list(eng_parser.parse('the quick brown fox jumps over the lazy dog'.split()))
for row in res[0].triples():
print(row)
chi_parser = StanfordDependencyParser(r'D:\data\stanford\software\stanford-parser-full-2020-11-17\stanford-parser.jar',
r'D:\data\stanford\software\stanford-parser-full-2020-11-17\stanford-parser-4.2.0-models.jar',
model_path=r'D:\data\stanford\software\stanford-parser-full-2020-11-17\chinesePCFG.ser.gz') # 这个文件要从stanford-parser-4.2.0-models.jar中解压出来得到
res = list(eng_parser.parse(['党', '是', '领导', '一切', '的', '。']))
print(list(res[0].triples()))
print('#' * 64)
for row in res[0].triples():
print(row)
dependency_demo()
句法树解析出错的情况很少(不足万分之一),除非句子特别长,需要单独做分句处理,但是依存关系出错的特别多(大概有30%的比例),原因是上面res[0].triples()
是一个空表,即没有解析得到任何结果,即便句子很短也会出现这种情况。比如上面这个句子['党', '是', '领导', '一切', '的', '。']
就没有解析结果,我猜想是因为句法不太符合常规,因此无法解析得到结果,但是这并不影响句法树的生成。我现在想到的一个办法就是给一个默认的依存关系解析结果(比如又ROOT指向所有节点),这样虽然不严谨,但是勉强应该也能用,关键是出错的位置太多了,实在是搞不清楚原因是什么(句法树和依存解析还都是用的一个jar包模型)。
2022-06-18~2022-06-19
- 疯狂挤了两天牙膏,然鹅距离完本还有很长的路,我想我可能是来不及了[苦涩]。
- 月跑破百,月底破150km应该没有问题。尽管每天都是35℃以上的高温,但是身体和腿都维持很好的状态,今天最快的一个分段配速达到4’08"/km,对于路跑来说已经很好。
- 开始适应前脚掌跑法,能够以4’20"的配速坚持到一公里以上,小腿力量依然吃紧。其实很多事情都是顺其自然,强求未必得用,以前看wxy前脚掌跑法又好又快,就试着去学,到头邯郸学步,连原先的节奏都找不到。现在每天练力量和体能,慢慢就能找到前脚掌的感觉了。
从aclanalogy上下载论文的脚本:
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
import os
import re
import pandas
import requests
from bs4 import BeautifulSoup
class AclanthologyCrawler:
def __init__(self):
self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
self.host = 'https://aclanthology.org'
self.bibtex_api = 'https://aclanthology.org/{paper_id}.bib'.format
self.pdf_api = 'https://aclanthology.org/{paper_id}.pdf'.format
self.regexs = [
r'<[^>]+>'
]
self.compilers = [re.compile(regex, re.I) for regex in self.regexs]
def easy_download(self, url):
print('Request for main page ...')
response = requests.get(url, headers={'User-Agent': self.user_agent})
print(' - Complete !')
html = response.text
soup = BeautifulSoup(html, 'lxml')
main_section = soup.find('section', id='main')
ps = main_section.find_all('p')
data_dict = {
'pdf': [],
'bib': [],
'title': [],
}
for p in ps:
links = p.find_all('a')
pdf_url = None
bib_url = None
for link in links:
if str(link.string).strip() == 'pdf':
pdf_url = link.attrs['href']
if str(link.string).strip() == 'bib':
bib_url = link.attrs['href']
break
title_link = p.find('strong').find('a', class_='align-middle')
title = self.compilers[0].sub('', str(title_link)).replace('\t', '').replace('\n', '')
data_dict['pdf'].append(pdf_url)
data_dict['bib'].append(bib_url)
data_dict['title'].append(title)
print('Saving dataframe ...')
dataframe = pandas.DataFrame(data_dict, columns=list(data_dict.keys()))
dataframe.to_csv('{}.csv'.format(url.strip('/').split('/')[-1]), sep='\t', header=True, index=False)
print(' - Complete !')
def _download_file(_url):
_response = requests.get(_url, headers={'User-Agent': self.user_agent})
with open(_url.strip('/').split('/')[-1], 'wb') as _f:
_f.write(_response.content)
for i in range(dataframe.shape[0]):
pdf_url = dataframe.loc[i, 'pdf']
bib_url = dataframe.loc[i, 'bib']
if pdf_url == pdf_url:
_download_file(pdf_url)
if bib_url == bib_url:
_download_file(self.host + bib_url)
def download_bibtex(self, paper_id, download_to='d:/'):
url = self.bibtex_api(paper_id=paper_id)
print(url)
response = requests.get(url, headers={'User-Agent': self.user_agent})
with open(download_to + f'{paper_id}.bib', 'wb') as f:
f.write(response.content)
if __name__ == '__main__':
ac = AclanthologyCrawler()
# url = 'https://aclanthology.org/events/emnlp-2020/'
# url = 'https://aclanthology.org/events/acl-2020/'
# ac.easy_download(url)
# paper_ids = list(map(lambda x: x.rstrip('.pdf'), os.listdir(r'D:\study\project\知识图谱相关研究及应用概述\reference\aclemnlp')))
# for paper_id in paper_ids:
# ac.download_bibtex(paper_id=paper_id)
# for root, dirname, filenames in os.walk(r'D:\study\course\学术论文写作\reference\acl-emnlp'):
# for filename in filenames:
# ac.download_bibtex(paper_id=filename.rstrip('.pdf'))
ac.download_bibtex(paper_id='2020.acl-main.49')
2022-06-20~2022-06-21
- 3km跑进12’50",相比巅峰期11’30"依然拉垮,考虑到高温和爬升,已然近一个月以来最好水平。最近四天都是同样的环湖路线,3km+1.3km+1km,间隔5分钟走路恢复,配速一般是4’20"转4’10"转4’20",其中第二个1.3km最痛苦,因为是在闹市街,人多,不得不全力顶完全程。
- 连日高温蒸煮,自从丹阳配完眼镜回来之后,就再没穿过上衣,跑步也是赤膊,跑不出成绩那就跑成镇上的传说。
- wyl早上居然还能腆着脸问我写得怎样,提前不到一周让我开始写,搞得申报书跟他一点关系没有,我把初稿发给他也不知道看没看,什么意见也没有,就告诉我明晚先交一份给学校审核,最终ddl在周五中午,真是服了,反正文献综述肯定是赶不完,今天画了一天图,晚上开始赶稿子,反正我是从写论文的立场做这篇本子,能不能过关我毛事,过了24号我该做自己的东西还是做自己的东西。
搜索引擎whoosh的用法示例:
whoosh 官方文档:https://whoosh.readthedocs.io/en/latest/
使用 whoosh 之前,需要先定义 index 对象,同时创建 schema对象 与 index 对应。schema中包含一列字段,这些字段存放在 index 中。每个字段都是文档中的一部分信息,例如标题和文本内容。字段能被搜索或者存储。
定义一个 schema,由两个字段:
from whoosh.fields import Schema, STORED, ID, KEYWORD, TEXT
schema = Schema(title=TEXT(stored=True), content=TEXT,
path=ID(stored=True), tags=KEYWORD,
icon=STORED)
我们仅需要创建一次 schema,当创建索引时,schema 会被序列化并与 index 保存在一起。
当创建 schema 对象时,需要指定字段名和其对应的类型,在 whoosh.fields 下,由如下类型:
ID:该类型索引字段的整个值作为一个单位类索引,而不是拆分成多个词
TEXT:该类型适用于文本数据的正文,它为文本建立索引并存储术语位置以允许短语搜索
NUMERIC:数值类型,可以存储整数或者浮点数
BOOLEAN:Boolean类型
DATETIME:适用于 datetime 对象
KEYWORD:适用于空格或者标点分割的关键字,类型数据能被索引和搜索但是不支持短语搜索(为了节省空间)
STORED:与文档存储在一起而不是与索引,该类型的数据不能被索引和搜索
schema对象创建完成后,可以使用 create_in 函数创建索引:
import os.path
from whoosh.index import create_in
from whoosh.fields import Schema, STORED, ID, KEYWORD, TEXT
schema = Schema(title=TEXT(stored=True), content=TEXT,
path=ID(stored=True), tags=KEYWORD,
icon=STORED)
if not os.path.exists("index"):
os.mkdir("index")
ix = create_in("index", schema)
创建索引时,会创建一个存储对象来保存 index 信息,通常,存储对象都会是 FileStorage,一种使用文件来存储索引的存储介质。也可以通过 open_dir() 来打开索引:
from whoosh.index import open_dir
ix = open_dir("index")
创建好 index 对象后,我们可以往里面添加文档。writer() 方法会返回一个 IndexWriter 对象,使用它可以向 index 中添加文档:
writer = ix.writer()
writer.add_document(title=u"My document", content=u"This is my document!",
path=u"/a", tags=u"first short", icon=u"/icons/star.png")
writer.add_document(title=u"Second try", content=u"This is the second example.",
path=u"/b", tags=u"second short", icon=u"/icons/sheep.png")
writer.add_document(title=u"Third time's the charm", content=u"Examples are many.",
path=u"/c", tags=u"short", icon=u"/icons/book.png")
writer.commit()
添加文档时,没有必要对所有字段都添加值;能被索引的 TEXT 字段必须要传入一个 unicode 类型的值,仅仅被存储而不用来索引的字段可以传入任何可被序列化的对象。
文档存储到 index 后,就可以进行索引了,索引之前首先要创建一个 search 对象:
searcher = ix.searcher()
search 对象的 search() 方法需要传入一个 Query 对象。可以直接钩爪一个 Query 对象,也可以使用QueryParse构造一个 Query 对象。将 Query 对象传入 search() 方法中,可以得到一个 Results 对象。
from whoosh.query import *
myquery = And([Term("content", u"apple"), Term("content", "bear")])
from whoosh.qparser import QueryParser
parser = QueryParser("content", ix.schema)
myquery = parser.parse(querystring)
results = searcher.search(myquery)
2022-06-22
- 给一位陌生人(CSDN@CuiDouDoux)解答了一下午问题,想想以后能做老师真的再好不过。
- 赶一天稿子,傍晚wyl来催我,好在除文献综述外我已经全部完稿,反正时间也不容许他做什么修改,一切都是我说了算了,明天赶最后文献综述的部分。
- 晚上状态并不好,有发烧的那种乏力感,可能是白天脑力强度太高的缘故,一直在画图和挤文字,但还是坚持出去完成3km+1.3km+1km的间歇量,第二段配速跑到4’07",第一段依然是4’17"的配速,最后一段4’12",真的是完全耗尽,回来洗个澡又满状态复活。
- 最近一周强度都特别高,每一段都是硬顶着跑完。等凉快的天气要拉一拉长距离。
今天整理期末考卷,需要批量word转pdf,这个按道理python是有办法批量处理的,我找了很多教程,有用doc2pdf的,win32com的,pdfkit的,但是始终不得行,报错是DDL找不到,搞不太清楚,可能是win32库的位数有问题?但还是记录一下相关的用法:
>>> import win32com
>>> from win32com.client import Dispatch
>>> word = Dispatch('Word.Application')
>>> doc = word.Documents.Open('C:/Users/Test/Desktop/out.docx')
>>> doc.SaveAs('C:/Users/Test/Desktop/output.pdf', 17)
>>> doc.Close()
>>> word.Quit()
>>> from docx2pdf import convert
>>> convert("C:/Users/Test/Desktop/out.docx", "C:/Users/Test/Desktop/output.pdf")
# 转换单个文件
docx2pdf myfile.docx
# 将一个目录下的word文档都转换成pdf文件
docx2pdf myfolder/
# -----------------
from win32com import client
import os
# 转换doc为pdf
def doc2pdf(fn):
word = client.Dispatch("Word.Application") # 打开word应用程序
doc = word.Documents.Open(fn) # 打开word文件
a = os.path.split(fn) # 分离路径和文件
b = os.path.splitext(a[-1])[0] # 拿到文件名
doc.SaveAs("{}\\{}.pdf".format(path1, b), 17) # 另存为后缀为".pdf"的文件,其中参数17表示为pdf
doc.Close() # 关闭原来word文件
word.Quit()
# 转换docx为pdf
def docx2pdf(fn):
word = client.Dispatch("Word.Application") # 打开word应用程序
doc = word.Documents.Open(fn) # 打开word文件
a = os.path.split(fn) # 分离路径和文件
b = os.path.splitext(a[-1])[0] # 拿到文件名
doc.SaveAs("{}\\{}.pdf".format(path1, b), 17) # 另存为后缀为".pdf"的文件,其中参数17表示为pdf
doc.Close() # 关闭原来word文件
word.Quit()
2022-06-23~2022-06-24
- 我是真的给wyl这老六整麻了,昨天赶文献综述到凌晨,早上八点不到就给他叫起来填网上的申请信息,整到下午三点我实在顶不住编了个谎,说要去看牙医溜出去跑了5km(湿热得离谱,就快交代在路上了。五点多狂风大作,久违地下了一场阵雨),回来洗澡补觉,醒来看到wyl发消息给我说,好像申请入口搞错了,今年是赶不上了,我要不是看在他六十多岁的份上,真的是要爆粗口了,两万多字全是我写,一点意见不给提,到头最后连起码的申请入口都没搞清楚,我还能说啥。
- 今天wyl还算有点良心给我打了个电话,算了不计较罢。
- 晚上老爸给我骑车破风,3km大突破,跑到12’24",均配4’08",步频提升到170。只可惜第二段间歇总想着坐车回家,1km就报销了。一整天坐着改卷子腿都麻了,但是一上路还是很有精神,年轻真好。
今天在金山文档批改期末试卷,搞了个从金山文档批量下文件的js脚本(非会员不能批量下载):
let groups = "1842648021";
let count = 54;
let res = await fetch(`https://drive.kdocs.cn/api/v5/groups/${groups}/files?include=acl,pic_thumbnail&with_link=true&offset=0&count=${count}&orderby=fname&order=ASC&filter=folder`);
let files = await res.json();
files = files.files;
let urls = []
let fid, info, url;
for (let f of files) {
fid = f.id;
res = await fetch(`https://drive.kdocs.cn/api/v5/groups/${groups}/files/${fid}/download?isblocks=false&support_checksums=md5,sha1,sha224,sha256,sha384,sha512`, {
"method": "GET",
"mode": "cors",
"credentials": "include"
});
info = await res.json();
url = info.url;
urls.push(url);
}
console.log("待下载文件数量:", urls.length);
for (let i = 0; i < urls.length; i++) {
let url = urls[i];
let fname = files[i].fname
fetch(url).then(res => res.blob().then(blob => {
let a = document.createElement('a');
let url = window.URL.createObjectURL(blob);
let filename = fname;
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}))
}
这个控制台脚本用于绕开VIP批量下载文件夹内所有文件:
-
进入待下载文件夹,找到标题栏中的team后面的数字,这是group_id,记做变量
groups
-
需要确定共享文件夹下有多少文件,下滑到底部,然后再全选,可以看到总数,比如这里总数是54,记做
count=54
-
新建一个标签页,通过右键菜单进入
检查
开发者工具,进入console
部分, 复制main.js中的内容到console中,修改其中的groups和count变量, 再回车即可下载所有文件。部分浏览器需要同意下载多个文件。
需要再金山文档先登陆过,该共享文件夹拥有下载权限,才可以使用该控制台脚本。
2022-06-25~2022-06-26
- 累死,这两天一直在外奔波,状态低迷,昨天3km报销,热得麻木无力,目前月跑132km,最后4天应该勉强能达标。
- 八月份打算去一趟三亚,如果没有不可抗力的话,确实也很久不出远门。
tqdm单根进度条不会重复显示的写法:
from tqdm import tqdm
def train(total, epoch):
pbar = tqdm(total=total, bar_format='{l_bar}{r_bar}', dynamic_ncols=True)
pbar.set_description('Epoch {}'.format(epoch))
for i in range(total):
fields = {
'loss': random.random(),
'accuracy': random.random(),
'recall': random.random(),
}
pbar.set_postfix(**fields)
pbar.update()
if __name__ == '__main__':
total = 1024
n_epoch = 16
for epoch in range(1, 1 + n_epoch):
train(total=total, epoch=epoch)
- 注意这种写法要求外循环体
for epoch in range(1, 1 + n_epoch)
中不含有print
的内容, 否则依然会输出很多行; set_description
可以直接用参数tqdm(response.iter_content(self.chunk_size), desc='Download process', total=total)
中的desc
参数取代- 不带有
__len__
方法的迭代器需要手动提供total
参数才能显示进度条。
2022-06-27~2022-06-29
- 前日三段间歇以4’18"+4’02"+4’18"的配速撑下来(中间一段跑到了历史最佳),昨天状态很差跑休,今天4’16"配速跑完4km,前2km起得太快,提到4’05",后面就跑不动了。本来晚上起风特别凉快,想要试试10km,好可惜。
- 行百里半九十,本来月半85km时以为这个月必上150km,结果今天结束只有144.18km,下旬明显怠惰了好多,跑休了三天,明天预报是暴雨,真希望下得下来,这么多天一滴雨没有已经快要热成肉干,跑步是越来越难以坚持得下去。明天要做本月最后一次冲击。
- wyl就特别有意思,之前申报打水漂之后,临时重整个新项目,又把我给拖下水,真是服了这个老六。
我这几天在看迁移学习的东西,找到一篇很好的综述arxiv@2201.05867,不过比较长,笔注在写,还要很长时间才能出。
其实迁移学习还是一个挺大的概念,不止是原先我们想象的那么狭隘(可能大家熟知的迁移学习都属于领域适应这个研究范畴,即从源领域学习模型迁移到目标领域),一切为了目标领域做的预训练工作都可以视为迁移学习的一部分,而且有很多很有趣的迁移学习方法,比如元学习(meta learning)和因果学习(casual learning),而且这部分理论性特别的强,比想象得要难很多,值得花时间好好钻研一下。
2022-06-30~2022-07-02
- 膝痛,29号晚上开始有点疼,30号偷了一天懒,昨天补量7.5km(5.1+1.4+1.0,间歇5分钟,配速4’26"+4’14"+4’26")。今天感觉疼得更厉害,只能转做力量训练,负重10kg,一组(32箭步走、16哑铃屈臂举高、16深蹲)做了三组。
- 真的好难好难,一个月高强度后有些厌跑。说起来已经大半年没有PB,中途也有很多阻碍,不知今年还能否再次PB。无论如何我都在期待着下半年会有比赛可以参加,虽然形势并不乐观。
一个关于内置sorted
方法的小技巧:
from typing import List
def sort_index(array: List[int]) -> (List[int], List[int]):
sorted_index_array = sorted(enumerate(array), key=lambda k: k[1])
sorted_index = [x[0] for x in sorted_index_array]
sorted_array = [x[1] for x in sorted_index_array]
return sorted_index, sorted_array
if __name__ == '__main__':
# import torch
# array = [.1, .3, .5, .7, .9, .2, .4, .6, .8, 1.]
# array = torch.abs(torch.FloatTensor([-.1, -.3, -.5, -.7, -.9, .2, .4, .6, .8, 1.]))
array = ['a', 'c', 'e', 'g', 'i', 'b', 'd', 'f', 'h', 'j']
sorted_index, sorted_array = sort_index(array)
print(sorted_index)
print(sorted_array)
2022-07-03~2022-07-04
- 难以置信,昨天ygf绕世纪公园跑了12圈,总计60km,6分配速,6个小时,真的是个无可救药的疯子,太TM强悍了。
- 相较之下自己却进入厌跑期,状态越来越拉垮,跑一休一权且恢复。昨天休整一日,今天开始重新闭关肝CAIL2021。
pdf解析图片:
# -*- coding: UTF-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
import cv2
import numpy
from PyPDF2 import PdfFileReader, PdfFileWriter
# 解析PDF中的图片
def parse_pdf_images(import_path: str) -> None:
with open(import_path, 'rb') as pdf:
reader = PdfFileReader(pdf)
for number in range(reader.getNumPages()):
page = reader.getPage(number)
print('Page {}:'.format(number))
x_object = page['/Resources'].get('/XObject')
if x_object is None:
continue
for key in x_object:
if x_object[key]['/Subtype'] == '/Image':
size = (x_object[key]['/Width'], x_object[key]['/Height'])
data = x_object[key].getData()
if x_object[key]['/ColorSpace'] == '/DeviceRGB':
mode = 'RGB'
else:
mode = 'P'
image = numpy.frombuffer(data, numpy.uint8)
print(x_object[key]['/Filter'])
if x_object[key]['/Filter'] == '/FlateDecode':
print(size)
print(image.shape)
image = image.reshape(size[1], size[0], 3)
cv2.imwrite('page' + '%05ui' % number + 'key' + key[1:] + '.png', image)
elif x_object[key]['/Filter'] == '/DCTDecode':
cv2.imwrite(key[1:] + '.jpg', image)
elif xObject[obj]['/Filter'] == '/JPXDecode':
cv2.imwrite(key[1:] + '.jpg', image)
2022-07-05~2022-07-06
matplotlib多组数据对应多根坐标轴作图示例:
# 选择题模型训练作图
def train_plot_choice(model_name,
train_logging_dataframe,
valid_logging_dataframe=None,
train_plot_export_path=None,
valid_plot_export_path=None):
plt.rcParams['figure.figsize'] = (12., 9.)
# 提取需要使用的数据
epochs = train_logging_dataframe['epoch']
losses = train_logging_dataframe['loss']
accuracys = train_logging_dataframe['accuracy']
strict_scores = train_logging_dataframe['strict_score']
loose_scores = train_logging_dataframe['loose_score']
# 横坐标为epoch, 但是要凑到跟其他数据一样的长度
xs = numpy.linspace(0, epochs.max(), len(epochs))
# 绘图
fig = plt.figure(1)
host = HostAxes(fig, [0.15, 0.1, .65, 0.8]) # 主图: [ 左,下,宽,高 ]
par1 = ParasiteAxes(host, sharex=host) # 右侧第一根轴: accuracy
par2 = ParasiteAxes(host, sharex=host) # 右侧第二根轴: strict_score
par3 = ParasiteAxes(host, sharex=host) # 右侧第二根轴: loose_score
host.parasites.append(par1)
host.parasites.append(par2)
host.parasites.append(par3)
host.set_ylabel('loss') # 主纵轴
host.set_xlabel('epoch') # 横坐标
host.axis['right'].set_visible(False) # 主图的右侧纵轴不显示: 要留给par1显示
par1.axis['right'].set_visible(True)
par1.set_ylabel('accuracy')
par1.axis['right'].major_ticklabels.set_visible(True)
par1.axis['right'].label.set_visible(True)
par2.set_ylabel('strict_score')
new_axisline = par2._grid_helper.new_fixed_axis
par2.axis['right2'] = new_axisline(loc='right', axes=par2, offset=(45, 0))
par2.axis['right2'].label.set_visible(True)
par3.set_ylabel('loose_score')
new_axisline = par3._grid_helper.new_fixed_axis
par3.axis['right4'] = new_axisline(loc='right', axes=par3, offset=(90, 0))
par3.axis['right4'].label.set_visible(True)
fig.add_axes(host)
p1, = host.plot(xs, losses, label='loss')
p2, = par1.plot(xs, accuracys, label='accuracy')
p3, = par2.plot(xs, strict_scores, label='strict_score')
p4, = par3.plot(xs, loose_scores, label='loose_score')
par1.set_ylim(0, 1.2)
par2.set_ylim(0, 1.2)
par3.set_ylim(0, 1.2)
host.axis['left'].label.set_color(p1.get_color())
par1.axis['right'].label.set_color(p2.get_color())
par2.axis['right2'].label.set_color(p3.get_color())
par2.axis['right2'].set_axisline_style('-|>', size=1.5) # 纵轴的小短横朝向左侧
par3.axis['right4'].label.set_color(p4.get_color())
par3.axis['right4'].set_axisline_style('-|>', size=1.5) # 纵轴的小短横朝向左侧
host.set_xticks(list(range(max(epochs) + 1)))
host.legend()
plt.title(f'Train Plot for {model_name}')
if train_plot_export_path is None:
plt.show()
else:
plt.savefig(train_plot_export_path)
plt.close() # 一定要close, 否则会重复画在一张图上
# 验证集情况作图
if valid_logging_dataframe is not None:
epochs = valid_logging_dataframe['epoch']
accuracys = valid_logging_dataframe['accuracy']
strict_scores = valid_logging_dataframe['strict_score']
loose_scores = valid_logging_dataframe['loose_score']
plt.plot(epochs, accuracys, label='accuracy')
plt.plot(epochs, strict_scores, label='strict_score')
plt.plot(epochs, loose_scores, label='loose_score')
plt.xlabel('epoch')
plt.ylabel('metric')
plt.title(f'Valid Plot for {model_name}')
plt.legend()
if valid_plot_export_path is None:
plt.show()
else:
plt.savefig(valid_plot_export_path)
plt.close() # 一定要close, 否则会重复画在一张图上
2022-07-07
- 35℃,80%湿度,又是不知死活的一天。下午三点,出门开跑。主要是最近晚上跑总感觉很乏力,想试试下午精神状态好的时候跑会不会好一点。然而理想的10km很丰满,现实的5km的间歇就很骨感。六月份训练状态一直都是高效训练,最大摄氧量一度超过60,现在跑起来真的是越来越沉重,七月份以来训练状态都是效率不佳,实话说有些灰心丧气。
- 嫌带水太重,就只拿了一瓶风油精上路,手机都没带。结果回到家涂在太阳穴上的风油精还没挥发完,半路肚子还突然疼,难受到就要交代在路上,真的是太想重回巅峰期,但是无论是身体素质还是天气都太难顶。无法想象那些这种天动辄二三十公里,虽然配速大都是五分以上,但也是足以令人艳羡了。
- 目前空腹体重71.5kg,其实也不算太重,也不知道为什么感觉步伐特别沉重,唉,管他呢,回来一根梦龙,白练就白练。
有人问我SageConv和GraphConv的区别,记录一下我的回答:
目前GNN的算法框架几乎都是消息计算(MC)和消息聚合(MA)两步走,MC与普通的神经网络前向传播没有区别,即根据上一层节点的特征计算下一层节点的特征;MA则是根据节点的邻接关系(一般来说取一级邻接,当然可以设计新的GNN基于新的邻接规则进行MA)对MC中计算得到的若干邻接节点特征进行聚合(比如取平均、求和、取最大值等)。具体而言,GraphConv可以理解为神经网络中的全连接层(Linear/Dense),它的MC就是用的全连接层的逻辑,MA则是一级邻接进行求和聚合;SageConv则更为灵活,它提供了多种不同的聚合方式,比如SUM,LSTM,POOL等。很多时候要多看官方文档里的说明,仔细阅读https://docs.dgl.ai/api/python/nn-pytorch.html就会发现SAGEConv和GraphConv有三个显著区别:
- Sage是先进行MA,然后再进行的MC;GraphConv则是先MC,再MA(求和/平均
- Sage会将上一层的节点特征拼接到下一层的节点特征中来,GraphConv则不会
- Sage还额外做了节点特征张量的归一化操作(norm)
2022-07-08~2022-07-10
- 晚上突然暴雨,感动的我眼泪都要流下来了。昨天傍晚小雨过后(地都没湿),外面闷得跟蒸笼一般,又是跑到脱水,特别难受,今天拜暴雨所赐终于可以跑休一天了,但估计明天还是一样热(苦涩)。
- 这两天突发奇想做了一些无用的证明工作,主要是关于蒋干盗书的策略问题,做完之后特别满足,感觉大脑又灵活了一些,都忘了要更新这篇博客了,随缘更呗。
- 最近作息不太好,中午睡不着觉,很难搞,要慢慢调整过来。
蒋干盗书的仿真代码(一):这玩意儿真的证了我好久,昨晚一直证到两点钟,睡觉都在想怎么证,结果到三点都没睡着。
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
# 蒋干盗书
EPSILON = 1e-8
# 生成参数
def generate_kwargs_for_color_list(color_list):
total_cards = sum(color_list) # 卡牌总数
total_colors = len(color_list) # 花色总数
sorted_remained_color_list = sorted(color_list, reverse=True) # 降序排列的各花色卡牌数
return {'total_cards' : total_cards,
'total_colors' : total_colors,
'sorted_remained_color_list': sorted_remained_color_list}
# 给定每种花色的卡牌数量, 计算偷对次数的数学期望
def get_expected_correct_guess(color_list, **kwargs):
if not len(kwargs):
kwargs = generate_kwargs_for_color_list(color_list=color_list)
# 计算辅助分布列与分布列
auxiliary_distribution_series = get_auxiliary_distribution_series(color_list=color_list, **kwargs)
distribution_series = get_distribution_series(color_list=color_list, auxiliary_distribution_series=auxiliary_distribution_series, **kwargs)
# 计算数学期望: 两种方法
# 1 根据辅助分布列计算数学期望
def _expectation_by_auxiliary_distribution_series(_auxiliary_distribution_series):
_expectation = 0.
_temp = 1.
for _auxiliary_probability in _auxiliary_distribution_series:
_temp *= _auxiliary_probability
_expectation += _temp
return _expectation
expectation_1 = _expectation_by_auxiliary_distribution_series(_auxiliary_distribution_series=auxiliary_distribution_series)
# 2 根据分布列计算数学期望
def _expection_by_distribution_series(_distribution_series):
_expectation = 0.
for i in range(len(_distribution_series)):
_expectation += i * distribution_series[i]
return _expectation
expectation_2 = _expection_by_distribution_series(_distribution_series=distribution_series)
# 检验正确性
assert abs(expectation_1 - expectation_2) < EPSILON
return expectation_1
2022-07-11~2022-07-14
- 前两日40℃高温,这辈子没见过4开头的温度,热得不敢出去跑步。今天降到37℃,终于还是逼自己去跑,总量不到5k的间歇,中间一个1.5km跑到了3’59"的配速,有那种跑强度间歇的感觉。
- 高百七月暑期跑量赛开始,其实月头就开始了,吴队直到11号才在群里通知。这帮MBA跑起步来是真不含糊,才三天就累积了335km,一下子跃居44个上海站队伍的第15名。可不是么,各种跑怪,上海41℃的天气也有人每天十几二十几公里的跑,上分能不快么。反正我才贡献了不到9km…
蒋干盗书的仿真代码(二):
# 上面函数的简约形式: 不计算分布列, 直接使用辅助分布列
def easy_get_expected_correct_guess(color_list, **kwargs):
if len(kwargs):
kwargs = generate_kwargs_for_color_list(color_list=color_list)
auxiliary_distribution_series = get_auxiliary_distribution_series(color_list=color_list, **kwargs)
def _expectation_by_auxiliary_distribution_series(_auxiliary_distribution_series):
_expectation = 0.
_temp = 1.
for _auxiliary_probability in _auxiliary_distribution_series:
_temp *= _auxiliary_probability
_expectation += _temp
return _expectation
expectation = _expectation_by_auxiliary_distribution_series(_auxiliary_distribution_series=auxiliary_distribution_series)
return expectation
# 只计算辅助分布列
def get_auxiliary_distribution_series(color_list, **kwargs):
if len(kwargs):
total_cards = kwargs['total_cards']
total_colors = kwargs['total_colors']
sorted_remained_color_list = kwargs['sorted_remained_color_list'][:]
else:
total_cards = sum(color_list) # 卡牌总数
total_colors = len(color_list) # 花色总数
sorted_remained_color_list = sorted(color_list, reverse=True) # 降序排列的各花色卡牌数
auxiliary_distribution_series = []
for i in range(total_cards):
auxiliary_probability = sorted_remained_color_list[0] / (total_cards - i)
auxiliary_distribution_series.append(auxiliary_probability)
sorted_remained_color_list[0] -= 1
sorted_remained_color_list.sort(reverse=True)
assert auxiliary_distribution_series[-1] == 1.
return auxiliary_distribution_series
# 只计算分布列
def get_distribution_series(color_list, auxiliary_distribution_series, **kwargs):
if len(kwargs):
total_cards = kwargs['total_cards']
total_colors = kwargs['total_colors']
else:
total_cards = sum(color_list) # 卡牌总数
total_colors = len(color_list) # 花色总数
distribution_series = []
temp = 1.
for i in range(total_cards + 1):
if i < total_cards:
probability = temp * (1 - auxiliary_distribution_series[i])
temp *= auxiliary_distribution_series[i]
else:
probability = temp * auxiliary_distribution_series[-1]
distribution_series.append(probability)
return distribution_series
2022-07-15~2022-07-17
- 我一直以为头孢加酒只是存在于玩梗中的事情,结果真的在我身上发生了。昨晚外出吃饭,其实昨天早上就有点流鼻涕,我是不想喝白酒的,但是在长辈的怂恿下只好喝了一杯(差不多二两),晚上回来喉咙疼老妈就让我吃了粒头孢,谁都没想起来头孢跟酒不能一起吃(主要是我自己也很少喝酒,在学校里只跟yy和wk分别去过一次酒吧,我自己TM也没想起来) 。然后就原地爆炸,午夜十二点开始发烧37.8℃,一开始还没意识到是中毒,到一点半已经烧到38.6℃,浑身上下疼到麻木,完全无法睡觉,起来关空调疯狂喝水,喝了得有五六杯,疯狂出汗,到凌晨三点终于降到37.2℃,总算是能迷迷糊糊能睡了。不想吵醒老妈就没去叫她。
- 一大早后知后觉,老妈赶紧把我带去医院,真的是服了,说起来也从医快三十多年,老妈居然都不知道头孢跟酒不能一起吃。然后各种离谱的症状,先是去内科听诊,医生发现心率特别慢(发热理论上心率会比较快),于是老妈就把我带到自己的科室去做了心电图和彩超,坐下来心率居然只有47(其实这倒也正常,本来我长年跑步,而且早上的确也不太发热了),而且T波非常高,心率非常不齐,老妈有点担心是心肌梗,又去抽血化验,化下来血小板偏低,胆红素非常高(肝脏在解毒),最关键的是肌酸激酶同工酶偏高,这同样说明有心肌炎,然后又带了个24小时动态心电图。现在满身插得电信号传导片,今天还不能洗澡,真的是倒大霉,这次就算是康复,身体肯定也是要受到极大的损伤了。
关于TreeRNN我已经有非常切实可行的做法,但是我暂时还不想详细说明,等结果出来再写博客专门讲解。具体而言,我决定为每个从句结构定义一个独立的神经网络模块,这样子我就避免因为不同语句句法树结构的不同而导致模型结构的不同了,本质上我只要学习模块的参数即可,这样即可统一所有模型。
2022-07-18~2022-07-19
- 后来家里的医生一致认为有病毒性心肌炎的风险,17号下午老妈本着以防万一的宗旨,硬是把我拖去江都住院。
- 我很是不满,发热这么严重,又极度缺乏休息,完全应该等我恢复好发热再去确定是否有心肌炎。我真的是很气,17号晚到18号打了两针退烧针,挂了6瓶水,烧都没能退得下去,放到以前发热是不可能挂水吃药,我自己喝水睡觉就能自愈,最多烧得厉害时吃粒布洛芬应急,老妈就一直拿我心率低和CKMB化验结果偏高不放,住院睡又睡不好,吃又吃不好,就为了防一个可能的心肌炎,拖垮整个身体,中央空调温度又不能调,汗又憋不出来。
- 今早心内科的专家龚金龙查房的时候,专门从头到尾询问了我这两天所有的历程,他指出虽然我24小时心电图心率最低点在早上10:31(38bpm),但是其实那时候我在睡觉,而心率最高点在早上11:16(119bpm),但其实那时候我在外面晒太阳,考虑到我长年跑步,最大摄氧量(59)远远超过一般成人水平(31~40),静息心率本身就不到50,因此心率的问题都是可解释的。尽管CKMB偏高,但是肌钙蛋白一直不高,不足以完全支撑确诊心肌炎,龚认为心肌炎的发现要从严,但是治疗要从宽,因为目前心肌炎并没有非常有效的确诊手段,最准确的方法是核磁共振,但是成本太高,而且一般的医院都没有这种条件。
- 最后我今早强烈要求出院,爬楼梯22层进病房以示抗议。老妈没办法最后拉我去她的老师那边做了最后的心脏彩超和B超,结果显示心脏除了三尖瓣轻微回流,也没有明显异常,根本就没有任何充分证据支撑有心肌炎,真的是花钱找罪受。中午回家,老妈还气得不行,明明就她晚上睡觉呼噜声最大,加上护士半夜老查房,根本睡不着。下午猛睡4小时,总算是恢复了元气。
注意到近期关于自动解题有一篇非常新的研究成果arxiv@2205.12615,这个是关于数学证明题的自动解答,研究机构是谷歌研究所和斯坦福大学,应该是相关领域的最高水平了,值得跟进。
目前我粗看了一下,有两点值得注意,他们的方法是定义类似XML和HTML的标记文档来格式化定理、公理、推论等以及题干信息,文档标记大多继承了LATEX数学公式中的写法,即以转化为代码的形式来进行处理,这样的好处有点类似我目前使用的句法结构树中的使用从句标签的思想,只是暂时还没有看他们到底是如何对标签进行处理的,我是计划直接对各个标签定义独立的网络模块,其实相对于文科题,这种理科题相对来说规则定义上确实要简单一些,至少我觉得CAIL是不能用这种公理化的表示来结构化题干和参考文档的,使用句法树已经是非常好的手段了。第二,注意到他们的结果提升其实很低,只是从28%提升到35%,非常搞笑的是他们宣称如果正确率能达到100%,他们有信心创造出获得IMO金牌的人工智能,我想在他们有生之年是看不到了,毕竟吹牛不用交税hh。
2022-07-20~2022-07-25
- 尘埃落定,昨天复查CKMB还是高,虽然比之前降了不少,但是心电图坐下来心率只有34bpm,老妈又跟打了鸡血一样,非要拉我去苏北医院找更好的心血管专家再查。然后今天去苏北,专家看了我之前所有的检查报告,人家明确指出常年长跑的人这种窦性心动过缓的心电图很正常,然后让再抽血查了一次心肌指标,包括CKMB的指标在内一点问题没有(这说明就是家里医院测得不准,参考区间0~2.2ug/ml,测下来才0.22ug/ml,根本就一点事情都没有),最后专家说了就是没有问题。这下老妈终于哑口无言,一周多以来天天吃白眼,真是受够了,我说了千遍百遍身体没有问题,就非要把几千块钱打水漂才肯心安。一周来跑东跑西,还不让我跑步,真的是要把我气死了。
- 我说你说我身上哪里有问题都好,非要说我心脏有问题,还说是跑步跑出来的,说起来一口一个心脏不是儿戏,出问题是大问题。我最大摄氧量59,是普通人的1.5~2倍,心率低一倍都不足为奇,再加上我从小练习吹奏乐器,小学时肺活量就超过4000,现在更是7000向上,这样的心肺功能用于长跑再合适不过,这辈子也不可能有人阻止得了我去跑马拉松。生命在于运动,从来没有无代价的事情,人的身体本身就是不断衰落的,硬要说跑步伤身,那是你非要忽视跑步带来的更多收益。
- 去苏北顺道拜访了旁边的扬州中学,我知道是进不去的,但是毕竟有五年没有返回母校了,也算是曾经的骄傲过的地方。门卫很有意思,一看到我就说我认识你,你肯定是学生,而且至少毕业四年以上了,我特别惊奇,竟然这么准,我也在这里住校了整整三年,不知道是话术,还是真的还记得我。
图模块相关处理,包括简单的词云以及句法树转networkx图的算法(类似标记文档解析):
# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
# 图模型相关工具函数
if __name__ == '__main__':
import sys
sys.path.append('../')
import os
import re
import dgl
import time
import json
import nltk
import warnings
import jieba.posseg as peg
from networkx import DiGraph, draw
from matplotlib import pyplot as plt
from nltk.parse.stanford import StanfordParser, StanfordDependencyParser
from setting import *
from src.graph_tools import *
from src.data_tools import load_stopwords, filter_stopwords
from src.utils import load_args, timer
# 2022/01/02 20:14:05 绘制不同法律门类的词云, 去除停用词, 默认保存到TEMP_DIR下
@timer
def plot_reference_wordcloud(export_root=TEMP_DIR):
reference_dataframe = pandas.read_csv(REFERENCE_PATH, sep='\t', header=0)
stopwords = load_stopwords(stopword_names=None)
for law, group_dataframe in reference_dataframe.groupby(['law']):
group_dataframe = group_dataframe.reset_index(drop=True)
contents = []
for i in range(group_dataframe.shape[0]):
content = ' '.join(filter_stopwords(tokens=eval(group_dataframe.loc[i, 'content']), stopwords=stopwords))
contents.append(content)
text = '\n'.join(contents)
wordcloud = WordCloud().generate(text=text)
wordcloud.to_file(os.path.join(TEMP_DIR, f'{law}.png'))
# 2022/03/01 13:56:32 生成词性标注, 目前使用jieba, 另外nltk.stanford句法解析包里也有词性标注的文件, 不过目前以jieba为准
@timer
def generate_pos_tags(sentence):
pos_tags = peg.lcut(sentence, use_paddle=True)
return pos_tags
# 2022/03/30 12:04:02 从句法树中调取词性标注, 经验证这个分词序列与原序列是完全相同的
# 2022/03/30 19:29:04 但是存在若干替换对应, 详见setting.py中的STANFORD_SPECIAL_SYMBOL_MAPPING字典中所示
def generate_pos_tags_from_parse_tree(parse_tree, regex_compiler = re.compile('\([^\(\)]+\)', re.I)):
if not isinstance(parse_tree, str):
# 2022/03/09 19:48:50 只对字符串形式的树进行处理, 原数据结构不便于处理
parse_tree = str(parse_tree)
results = regex_compiler.findall(parse_tree)
pos_tags = list(map(lambda result: tuple(result[1: -1].split(' ', 1)), results))
return pos_tags
# 2022/03/07 16:05:41 生成句法解析树, 使用的是stanford-parser-full-2020-11-17
@timer
def generate_parse_tree(tokens, language='chinese'):
parser = StanfordParser(STANFORD_PARSER_MODEL_SUMMARY['jar'],
STANFORD_PARSER_MODEL_SUMMARY['models'],
model_path=STANFORD_PARSER_MODEL_SUMMARY['pcfg'][language])
parse_tree = list(parser.parse(tokens))
return parse_tree
# 2022/06/12 10:46:20 生成依存关系, 使用的是stanford-parser-full-2020-11-17
@timer
def generate_dependency(tokens, language='chinese'):
parser = StanfordDependencyParser(STANFORD_PARSER_MODEL_SUMMARY['jar'],
STANFORD_PARSER_MODEL_SUMMARY['models'],
model_path=STANFORD_PARSER_MODEL_SUMMARY['pcfg'][language])
dependency = list(parser.parse(tokens))
return dependency
# 2022/04/28 23:30:39 改进后的parse_tree_to_graph函数: 使用编号命名节点, 将树节点的内容保存在节点数据中, 参数display表示是否可视化句法树
# 2022/05/20 09:35:22 添加新参数return_type, 用于区分返回networkx图与dgl图
# 2022/05/20 12:43:23 添加新参数ignore_text, 用于区分是否保留叶子节点上的文本内容
def parse_tree_to_graph(parse_tree, display=False, return_type='networkx', ignore_text=False):
assert return_type in ['networkx', 'dgl'], f'Unknown param `return_type`: {return_type}'
if not isinstance(parse_tree, str):
# 2022/03/09 19:48:50 只对字符串形式的树进行处理, 原数据结构不便于处理
warnings.warn(f'句法解析树应为字符串, 而非{type(parse_tree)}')
parse_tree = str(parse_tree)
graph = DiGraph() # 2022/03/09 20:48:52 绘制句法树的有向图
current_index = 0 # 2022/03/09 20:48:52 记录已经解析到句法树字符串的位置
stack = [] # 2022/03/09 20:48:52 用于存储句法树节点的栈(包括句法树标签与文本)
node_id = -1 # 2022/04/23 16:09:19 全局记录节点的id, 考虑将节点信息存储在node.data中
# 2022/04/28 23:29:29 合并后的添加节点函数
def _add_node(_node_id, _text, _tag, _is_pos, _is_text):
_node_data = {
'node_id' : _node_id, # 2022/05/20 12:48:02 节点编号: 0, 1, 2, ...
'text' : _text, # 2022/05/20 12:48:02 文本分词内容, 若_is_text为False, 则为None
'tag_id' : STANFORD_SYNTACTIC_TAG_INDEX.get(_tag, -1), # 2022/05/20 12:48:02 句法树标签内容, 若_is_text为True, 则为None, 否则必然属于STANFORD_SYNTACTIC_TAG集合
'is_pos' : _is_pos, # 2022/05/20 12:48:02 记录是否是句法树叶子节点上的词性标注
'is_text' : _is_text, # 2022/05/20 12:48:02 记录是否是句法树叶子节点上的文本分词
}
graph.add_node(node_for_adding=_node_id, **_node_data) # 添加新节点
if stack: # 若栈不为空
_stack_top_node_id = stack[-1]['node_id'] # 取栈顶节点的编号
graph.add_edge(u_of_edge=_stack_top_node_id, v_of_edge=_node_id) # 则栈顶节点(即当前分支节点)指向新节点
elif _is_text:
raise Exception('叶子节点文本内容找不到父节点标签')
if not _is_text:
stack.append(_node_data) # 最后将新节点(非叶/非文本)添加到栈中
while current_index < len(parse_tree):
# 左括号意味着新分支的开始
if parse_tree[current_index] == '(':
next_left_parenthese_index = parse_tree.find('(', current_index + 1) # 寻找下一个左括号的位置
next_right_parenthese_index = parse_tree.find(')', current_index + 1) # 寻找下一个右括号的位置
if next_left_parenthese_index == -1 and next_right_parenthese_index == -1:
# 左括号后面一定还有括号
raise Exception('句法树括号位置不合规')
if next_left_parenthese_index < next_right_parenthese_index and next_left_parenthese_index >= 0:
# 向右检索最先遇到左括号: 新节点出现
new_node = parse_tree[current_index + 1: next_left_parenthese_index].replace(' ', '') # 向右搜索先遇到左括号: 发现新节点
# 2022/05/20 13:00:30 新增断言
assert new_node in STANFORD_SYNTACTIC_TAG and new_node not in STANFORD_POS_TAG, f'Unknown syntactic tags: {new_node}'
node_id += 1 # 更新节点编号
_add_node(_node_id=node_id, _text=None, _tag=new_node, _is_pos=False, _is_text=False) # 添加新节点
current_index = next_left_parenthese_index # 将current_index刷新到新的左括号处
else:
# 向右检索最先遇到右括号: 此时到达叶子节点
leave_node = parse_tree[current_index + 1: next_right_parenthese_index] # 向右搜索先遇到右括号: 此时意味着已经到达叶子节点
new_node, text = leave_node.split(' ', 1) # 叶子节点由词性标注与对应的文本内容两部分构成
# 2022/05/20 13:00:30 新增断言
assert new_node in STANFORD_POS_TAG, f'Unknown pos tags: {new_node}'
node_id += 1 # 更新节点编号
_add_node(_node_id=node_id, _text=None, _tag=new_node, _is_pos=True, _is_text=False) # 添加叶子节点
if not ignore_text:
node_id += 1 # 更新节点编号
_add_node(_node_id=node_id, _text=text, _tag=None, _is_pos=False, _is_text=True) # 添加叶子节点上的文本内容
current_index = next_right_parenthese_index + 1 # 将current_index刷新到右括号的下一个位置
stack.pop(-1) # 弹出栈顶节点, 即叶子节点
elif parse_tree[current_index] == ')': # 空格则跳过
stack.pop(-1)
current_index += 1
elif parse_tree[current_index] == ' ': # 空格则跳过
current_index += 1
else: # 理论上不会出现其他情况, 除非字符串根本就不是一棵合法的句法树
raise Exception(f'不合法的字符: {parse_tree[current_index]}')
if display:
draw(graph, with_labels=True)
plt.show()
if return_type == 'networkx':
return graph
elif return_type == 'dgl':
return dgl.from_networkx(graph, node_attrs=['node_id', 'tag_id', 'is_pos', 'is_text'])
2022-07-26~2022-07-30
- 差不多折腾近两周,什么病也没有,老妈气得已经三四天不理我了,真的是无可奈何,烦得很。
- 下午重新开始跑步,不出意外又是从头来过,3km胸口就开始疼痛,停下来走了十几分钟心率都没恢复到130以下,头也隐隐作痛,估摸着是撑不住。最后冲了1km收尾,肌肉都快要失去记忆了。
- WXY又去给同学做伴郎,说起来他也快一个月不跑步了。高百的排名上财已经稳稳占据第五,累积跑量3300km,算起来还不到20天,实在是惊人。
最近做得一些关于gpytorch的demo:这个其实也可以用scipy复现
import os
import math
from matplotlib import pyplot as plt
import torch
import gpytorch
from gpytorch import kernels, means, models, mlls, settings
from gpytorch import distributions as distr
from IPython.display import Markdown, display
def printmd(string):
display(Markdown(string))
train_x = torch.linspace(0, 1, 100)
train_y = torch.sin(train_x * (2 * math.pi)) + torch.randn(train_x.size()) * 0.2
# We will use the simplest form of GP model, exact inference
class ExactGPModel(gpytorch.models.ExactGP):
def __init__(self, train_x, train_y, likelihood):
super(ExactGPModel, self).__init__(train_x, train_y, likelihood)
self.mean_module = gpytorch.means.ConstantMean()
self.covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel())
def forward(self, x):
mean_x = self.mean_module(x)
covar_x = self.covar_module(x)
return gpytorch.distributions.MultivariateNormal(mean_x, covar_x)
# initialize likelihood and model
likelihood = gpytorch.likelihoods.GaussianLikelihood()
model = ExactGPModel(train_x, train_y, likelihood)
for param_name, param in model.named_parameters():
print(f'Parameter name: {param_name:42} value = {param.item()}')
raw_outputscale = model.covar_module.raw_outputscale
print('raw_outputscale, ', raw_outputscale)
# Three ways of accessing the raw outputscale constraint
print('\nraw_outputscale_constraint1', model.covar_module.raw_outputscale_constraint)
printmd('\n\n**Printing all model constraints...**\n')
for constraint_name, constraint in model.named_constraints():
print(f'Constraint name: {constraint_name:55} constraint = {constraint}')
printmd('\n**Getting raw outputscale constraint from model...**')
print(model.constraint_for_parameter_name("covar_module.raw_outputscale"))
printmd('\n**Getting raw outputscale constraint from model.covar_module...**')
print(model.covar_module.constraint_for_parameter_name("raw_outputscale"))
raw_outputscale = model.covar_module.raw_outputscale
constraint = model.covar_module.raw_outputscale_constraint
print('Transformed outputscale', constraint.transform(raw_outputscale))
print(constraint.inverse_transform(constraint.transform(raw_outputscale)))
print(torch.equal(constraint.inverse_transform(constraint.transform(raw_outputscale)), raw_outputscale))
print('Transform a bunch of negative tensors: ', constraint.transform(torch.tensor([-1., -2., -3.])))
# Recreate model to reset outputscale
model = ExactGPModel(train_x, train_y, likelihood)
# Inconvenient way of getting true outputscale
raw_outputscale = model.covar_module.raw_outputscale
constraint = model.covar_module.raw_outputscale_constraint
outputscale = constraint.transform(raw_outputscale)
print(f'Actual outputscale: {outputscale.item()}')
# Inconvenient way of setting true outputscale
model.covar_module.raw_outputscale.data.fill_(constraint.inverse_transform(torch.tensor(2.)))
raw_outputscale = model.covar_module.raw_outputscale
outputscale = constraint.transform(raw_outputscale)
print(f'Actual outputscale after setting: {outputscale.item()}')
# Recreate model to reset outputscale
model = ExactGPModel(train_x, train_y, likelihood)
# Convenient way of getting true outputscale
print(f'Actual outputscale: {model.covar_module.outputscale}')
# Convenient way of setting true outputscale
model.covar_module.outputscale = 2.
print(f'Actual outputscale after setting: {model.covar_module.outputscale}')
print(f'Actual noise value: {likelihood.noise}')
print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}')
likelihood = gpytorch.likelihoods.GaussianLikelihood(noise_constraint=gpytorch.constraints.GreaterThan(1e-3))
print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}')
## Changing the constraint after the module has been created
likelihood.noise_covar.register_constraint("raw_noise", gpytorch.constraints.Positive())
print(f'Noise constraint: {likelihood.noise_covar.raw_noise_constraint}')
# Registers a prior on the sqrt of the noise parameter
# (e.g., a prior for the noise standard deviation instead of variance)
likelihood.noise_covar.register_prior(
"noise_std_prior",
gpytorch.priors.NormalPrior(0, 1),
lambda module: module.noise.sqrt()
)
# Create a GaussianLikelihood with a normal prior for the noise
likelihood = gpytorch.likelihoods.GaussianLikelihood(
noise_constraint=gpytorch.constraints.GreaterThan(1e-3),
noise_prior=gpytorch.priors.NormalPrior(0, 1)
)
# We will use the simplest form of GP model, exact inference
class FancyGPWithPriors(gpytorch.models.ExactGP):
def __init__(self, train_x, train_y, likelihood):
super(FancyGPWithPriors, self).__init__(train_x, train_y, likelihood)
self.mean_module = gpytorch.means.ConstantMean()
lengthscale_prior = gpytorch.priors.GammaPrior(3.0, 6.0)
outputscale_prior = gpytorch.priors.GammaPrior(2.0, 0.15)
self.covar_module = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.RBFKernel(
lengthscale_prior=lengthscale_prior,
),
outputscale_prior=outputscale_prior
)
# Initialize lengthscale and outputscale to mean of priors
self.covar_module.base_kernel.lengthscale = lengthscale_prior.mean
self.covar_module.outputscale = outputscale_prior.mean
def forward(self, x):
mean_x = self.mean_module(x)
covar_x = self.covar_module(x)
return gpytorch.distributions.MultivariateNormal(mean_x, covar_x)
likelihood = gpytorch.likelihoods.GaussianLikelihood(
noise_constraint=gpytorch.constraints.GreaterThan(1e-2),
)
model = FancyGPWithPriors(train_x, train_y, likelihood)
hypers = {
'likelihood.noise_covar.noise': torch.tensor(1.),
'covar_module.base_kernel.lengthscale': torch.tensor(0.5),
'covar_module.outputscale': torch.tensor(2.),
}
model.initialize(**hypers)
print(
model.likelihood.noise_covar.noise.item(),
model.covar_module.base_kernel.lengthscale.item(),
model.covar_module.outputscale.item()
)
2022-07-31~2022-08-31(完)
- 下午一点开始从宁远搬家,一直到晚上八点整完一切才发现电脑充电器忘拿,于是改完期末卷子连夜赶回嘉定,做个3号线都能把方向搞反,我想自己一定是与世隔绝太久了。
- 这一个月,大病初愈后就是极端酷暑,停跑也不至于,但基本上是一周跑一两次的水平,直到23号转凉才慢慢开始恢复。说到底一方面是wyl那边申到课题后破事太多,居然昨天刚下高铁告诉我今早监考期末考试,顺带把试卷做一遍写份答案出来,我TM直接问号脸。另一方面自己太懒,越发地厌跑,加上酷暑难耐索性就摆烂不练。
- 三门路并没有想象那么好,虽然也不算差,大套房里2个双人间,1个单人间,月初缴纳水电,然鹅楼里连饮水机都没有,还不让烧水,即便以后绝大多数时间不会在宿舍,但也对我这种每天要喝七八杯水的人太不友好。别的我也不想多吐槽,总之三门路更像是一个校外的廉价租房点,我们也并不是传统意义上的学生了,各种意义上的无奈。
重回长安后,莫名伤感。
如果只是写到这里结束,那的确是很令人失望。尽管我不愿意去承认,但更像是在逃避。临走前我把还剩小半本的日记留存家中,想重新写一本新日记,一切本不该如此。
围城,不见长安。
(本文完)