我也不知道为什么要看别人写的代码,我并没有碰到问题,我只是觉得自己的知识和代码架构能力以及那种在码代码时候的一种直觉少了许多,所以要看一些别人的代码,在看的时候不能浅尝辄止,借鉴api,借鉴一些类库,借鉴一些架构的方式,看看别人的思路。
源文件为txt 文件,使用python内置的open
函数打开文件,然后读取,并进行划分为list.
opt.src_data = open(opt.src_data).read().strip().split('\n')
使用spacy tokenize
方法,并使用re表达式
去除一些特殊的字符,然后运用列表推导式返回
如下所示:
return [tok.text for tok in self.nlp.tokenizer(sentence) if tok.text != " "]
fields
可以load weight
SRC
的fields
没有sos
和eos
使用了f-string,f‘hello world{var}'
使用了序列化来 加载 weights
, pickle.load
函数
global
修饰的变量 :全局变量,python 中若要修改肯定是修改局部变量,加上关键字,例global a = 1
,就是修改全局变量batch_size_fn
一种动态处理的东西,详看文档opt checkpoint
是什么load_weight
是否为空,若不为空,就加载出来,在创建数据集的时候,检查load_weight
是否为空,若为空就重新调用 build_vocab
,并检查checkpoint
值,若不为0 就dump field进去 if opt.load_weights is None:
SRC.build_vocab(train)
TRG.build_vocab(train)
if opt.checkpoint > 0:
try:
os.mkdir("weights")
except:
print("weights folder already exists, run program with -load_weights weights to load them")
quit()
pickle.dump(SRC, open('weights/SRC.pkl', 'wb'))
pickle.dump(TRG, open('weights/TRG.pkl', 'wb'))
pe = torch.zeros(max_seq_len, d_model)
for pos in range(max_seq_len):
for i in range(0, d_model, 2):
pe[pos, i] = \
math.sin(pos / (10000 ** ((2 * i)/d_model)))
pe[pos, i + 1] = \
math.cos(pos / (10000 ** ((2 * (i + 1))/d_model)))
pe = pe.unsqueeze(0)
self.register_buffer('pe', pe)
pe = Variable(self.pe[:,:seq_len], requires_grad=False)
if x.is_cuda:
pe.cuda()
transpose
方法进行 维度变换,没必要非得permute
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(d_k)
transpose permute
之后,只是改变了stride
,并没有修改底层数组,如果执行t2.view(-1)
要不报错,要不还是返回顺序的0-11.不是我们想要的
>>>t = torch.arange(12).reshape(3,4)
>>>t
tensor([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>t.stride()
(4, 1)
>>>t2 = t.transpose(0,1)
>>>t2
tensor([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
>>>t2.stride()
(1, 4)
>>>t.data_ptr() == t2.data_ptr() # 底层数据是同一个一维数组
True
那么,这个时候,contiguous
就派上用场啦!
>>>t3 = t2.contiguous()
>>>t3
tensor([[ 0, 4, 8],
[ 1, 5, 9],
[ 2, 6, 10],
[ 3, 7, 11]])
>>>t3.data_ptr() == t2.data_ptr() # 底层数据不是同一个一维数组
False
Parameters are Tensor subclasses, that have a very special property when used with Module s - when they’re assigned as Module attributes they are automatically added to the list of its parameters, and will appear e.g. in parameters() iterator
我们注册这两个parameter
self.alpha = nn.Parameter(torch.ones(self.size))
self.bias = nn.Parameter(torch.zeros(self.size))
norm = self.alpha * (x - x.mean(dim=-1, keepdim=True)) \
/ (x.std(dim=-1, keepdim=True) + self.eps) + self.bias
if opt.load_weights is not None:
print("loading pretrained weights...")
model.load_state_dict(torch.load(f'{opt.load_weights}/model_weights'))
else:
for p in model.parameters():
if p.dim() > 1:
nn.init.xavier_uniform_(p)
import argparse
parser = argparse.ArgumentParser(description="argparser 测试");
parser.add_argument('integers', nargs="+", type=str,help="传入一个参数");
args = parser.parse_args()
print(args)
print(args.integers)
结果
C:\Users\qiuqiu\PycharmProjects\Transformer_low_level\copy_tfm>python test1.py -h
usage: test1.py [-h] integers [integers ...]
argparser 测试
positional arguments:
integers 传入一个参数
optional arguments:
-h, --help show this help message and exit
C:\Users\qiuqiu\PycharmProjects\Transformer_low_level\copy_tfm>python test1.py 1 2 3 4
Namespace(integers=['1', '2', '3', '4'])
['1', '2', '3', '4']
2.位置参数对位置要求比较严格,所以有了可选参数
parser.add_argument("--family",type=str,help="姓")
parser.add_argument("--name", type=str,help="名")
args = parser.parse_args()
print(args)
#print(args.integers)
print(args.family + args.name)
结果
C:\Users\qiuqiu\PycharmProjects\Transformer_low_level\copy_tfm>python test1.py -h
usage: test1.py [-h] [--family FAMILY] [--name NAME]
argparser 测试
optional arguments:
-h, --help show this help message and exit
--family FAMILY 姓
--name NAME 名
C:\Users\qiuqiu\PycharmProjects\Transformer_low_level\copy_tfm>python test1.py -h
usage: test1.py [-h] [--family FAMILY] [--name NAME]
argparser 测试
optional arguments:
-h, --help show this help message and exit
--family FAMILY 姓
--name NAME 名
C:\Users\qiuqiu\PycharmProjects\Transformer_low_level\copy_tfm>python test1.py --family=张 --name=三
Namespace(family='张', name='三')
张三
3.另有 action default等参数另可探索
store_true,store_false 等,只要在命令行中加入了这个关键字(无需赋值),便会将相应参数变为 True False
4. 对于 args 来说,可以使用args.param (例如:args.device=1)添加新的属性,在被调用的函数中对args的修改也会直接修改args
可以自己写函数,也可以使用pytorch中的lr_schedular
这篇博客很详细
准备自己先写出来,然后再去参考他的代码!!!
时间隔得有点久,这几日心情可以说是七上八下,说一句矫情的话:
如今初识这世间,万般流连
# 维持三个变量,e_outputs,outputs,log_scores
# outputs 维度(beam_size,max_len) e_outputs(beam_size,seq_len,d_model)
outputs, e_outputs, log_scores = init_vars(src, model, SRC, TRG, opt)
#然后,求出累加的k个最好结果
outputs, log_scores = k_best_outputs(outputs, out, log_scores, i, opt.k)
进入k_best_outputs函数
# 求k个结果的:最坏的情况就是当前最好的结果是由一个beam产生
# 使用top_k函数,求出每一个beam的top_k结果
probs, ix = out[:, -1].data.topk(k)
# 然后累加到当前beam
log_probs = torch.Tensor([math.log(p) \
for p in probs.data.view(-1)]).view(k, -1) \
+log_scores.transpose(0,1)
#这里有一点不对的事,没根据当前当前是否产生了end_token进行累加
k_probs, k_ix = log_probs.view(-1).topk(k)
row = k_ix // k
col = k_ix % k
outputs[:, :i] = outputs[row, :i]
outputs[:, i] = ix[row, col]
log_scores = k_probs.unsqueeze(0)
# 每一步判断一下是否产生end_token,然后根据end_token 及时更正log_score的更新。
1.到处可见的断言 assert,当后面的条件为错误的时候抛出一个异常,而不至于使程序崩溃掉。到处可见的 try except,例如打开文件时。
2.pickle
判断load_weights
是否需要,使用pickle.load
函数进行加载
pickle.load 函数,对于词汇,如果是一开始跑,要进行pickle.dump
3.get_model
函数
在get_model
中创建并初始化并加载模型,并将模型放在GPU上面
4.对于有多层相同的,进行深复制:
def get_clones(module, N):
return nn.ModuleList([copy.deepcopy(module) for i in range(N)])
5.模型的架构分的很清楚:model layer sublayer embedding 各司其职,对于预测,也要写到不同的文件里,最好一个文件一个功能吧!
只有TRG 需要init_token
和eos_token