Coding随手记

  1. 序列标注任务,或者说是基于bert的任务,尽量手动convert_tokens_to_id,如果使用encode_plus可能在处理数据时产生错误。比如序列标注任务时,在语句中添加的[CLS]和[SEP]后,标签可能会和原句不对齐
  2. tokenizer.tokenize(some_word)返回的结果是列表,可以使用list.extend将返回结果写入tokens : list 中

  1. 变长padding,感觉可以在get_examples阶段进行。读取所有数据后先根据长度排序后再返回。
  2. lens=[所有数据长度],len_index = np.argsort(lens),然后data_list = data_list[index]即可,让-1*lens见鬼去吧。
  3. 在collate_fn之前要先处理好数据长度问题,如果数据长度超过max_length这里也没办法处理。因为到collate_fn的时候已经加好了[CLS]和[SEP],不能使用截断操作了。
  4. TorchCRF的crf的是有mask参数的,mask=attention_mask.byte()就有作用了。

  1. list.extend(tokenizer.unk_token) 会添加’[‘, ‘U’, ‘N’, ‘K’, ‘]’,但是extend([tokenizer.unk_token])则添加的是”[UNK]”

  1. torch.nn.utils.RNN.pack_padded_sequence
    原文链接:https://www.cxyzjd.com/article/kejizuiqianfang/100835528
    假设存在两条数据:
    1
    2
    tensor([[1, 2, 3, 4, 5, 6, 7],
    [2, 3, 4, 5, 6, 7, 0]])
    输入RNN的其实是[1,2],[2,3],[3,4],[4,5],[5,6],[6,7],[7,0],最终输入模型的0并非真实数据,若参与运算则会影响模型效果且浪费算力,于是使用torch.utils.nn.RNN.pack_padded_sequence方法(后称“pack”)去除掉输入进模型的padding标记,上述两条数据pack之后得到的结果为:
    1
    2
    3
    4
    PackedSequence(data=tensor([1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7]), batch_sizes=tensor([2, 2, 2, 2, 2, 2, 1]), sorted_indices=None, unsorted_indices=None)
    PackedSequence(data=tensor([3, 4, 4, 5, 5, 6, 6, 7, 7]), batch_sizes=tensor([2, 2, 2, 2, 1]), sorted_indices=None, unsorted_indices=None)
    PackedSequence(data=tensor([5, 6, 6, 7, 7]), batch_sizes=tensor([2, 2, 1]), sorted_indices=None, unsorted_indices=None)
    PackedSequence(data=tensor([7]), batch_sizes=tensor([1]), sorted_indices=None, unsorted_indices=None)
    暂且只看第一条数据,pack后的数据为:[1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7],实际上就是去除掉两条数据中padding标记的结果,RNN通过batch_sizes中的数字决定每次输入模型“几个数”,这里的batch_sizes为tensor([2, 2, 2, 2, 2, 2, 1]),意为连续取6次两位数,在第7次时只取一个数。

  1. Python中的矩阵乘法
    Python中,星乘(*)指两个矩阵对应位置相乘;点乘(.dot)指数学上的矩阵乘法。
    在进行星乘时,Python会对“低维度的矩阵”进行广播操作,使之能与另一矩阵维度匹配,而后进行对应位置相乘。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #假设现有一文本序列
    seq = torch.tensor(np.random.randint(0,2,(batch,seq_len)))
    #以及该序列所对应的词向量矩阵
    vector_mat = torch.tensor(np.random.randn(batch,seq_len,dim))
    #现要对padding的部分做mask
    mask = seq.unsqueeze(-1)
    mask = torch.tensor(mask>torch.tensor(np.array([0])),dtype=torch.float)
    print(seq)
    print("**********")
    print(mask)
    print("**********")
    print(vector_mat * mask)

  1. plt.subplots
    如果想要创建多个子图,可以使用plt.subplots方法创建“画布”。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    from matplotlib import pyplot as plt
    #设置一块 2*2 的画布,figsize设置子图?整个画布?的尺寸
    f,ax = plt.subplots(nrows=2,ncols=2,figsize=(18,12))
    #此时ax是一个多维的变量,可以通过flatten变成普通list索引访问
    ax = ax.flatten()
    #假设此时要画3个hist,可是定义的画布是2*2的,也就是可以画4个图,最后一个会显示空白
    ax[0].hist(train_text_lens)
    ax[0].set_title("train_text_lens")
    ax[1].hist(dev_text_lens)
    ax[1].set_title("dev_text_lens")
    ax[2].hist(test_text_lens)
    ax[2].set_title("test_text_lens")
    #想要删除某个子图可以使用plt.delaxes方法
    plt.delaxes(ax[-1])

  1. AutoTokenizer
    项目中AutoTokenizer较为常见,与特定的Tokenizer(类似于BertTokenizer)的区别在于,直接使用特定Tokenizer时,cache路径下不需要包括tokenizer.json文件。但是如果使用AutoTokenizer,cache中需要包含的就不仅仅是vocab、model.bin、config.json三个文件了,还需要一个类似于tokenizer.json的文件。

1
2
import re
re.finditer()#找到字符串中的所有目标字符
  1. json.load()和json.loads()的区别
    json.loads()将字符串读取成Python数据结构,json.load()将文件读取成Python数据结构
    另外,如果键值对中包含整型数据,将其保存为json后,会将所有的数据转化为字符串。可以使用object_hook参数来解决这个问题。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import json
    path = "something/something.json"

    def jsonKey2int(x):
    if isinstance(x,dict):
    return {int(k) : v for k,v in x.items()}
    return x
    with open(path,encoding="utf-8") as f:
    json.load(f,obgject_hook=jsonKey2int)

  2. pytorch中nn.CrossEntropyLoss()的计算过程中是包含Softmax
    如果在模型forward过程中自己计算softmax然后再使用交叉熵很可能会导致loss不下降
    @生命奇点_ZRY 用交叉熵完成分类任务,线性层的输出可别softmax了喔

  1. 引包
    小项目的工作路径中直接包含若干python文件或包,编写代码时可以直接调用。如果项目规模较大,在工作路径中包含若干子项目时,可能会涉及到不同包下模块的引用,经常会出现无法引用自己写的块的问题。

    –project
    —-main.py
    —-folder1
    – – test1.py
    – – test3.py


—-folder2
– – test2.py
– – test4.py
最外层文件夹名称project,包含main.py和文件夹folder1、folder2。folder1中包含test1.py和test3.py,folder2中包含test2.py和test4.py。
想要正常import,模块必须在sys.path中能被找到,import的查找顺序为:

  • 内置模块
  • .py文件所在目录(当前工作路径)
  • 环境变量中列出的目录(虚拟环境)
  • pip 或 easy_install安装的包
    可以通过打印sys.path的方式查看当前.py文件中sys.path包含了哪些内容
    1
    2
    3
    import sys
    for i in sys.path:
    print(i)
    假设当前执行test1.py,打印其sys.path,可以发现folder1是在其中的,此时若需要引用folder2中的模块,那必然是会报错的,但是由于Pycharm等IDE可将floder2设置为source root则可以避免报错,使用cmd执行时就会报错。

如何解决?

  • sys.path.append(“..”)
    想要调用父级目录中的模块时,可以将父级目录添加到sys.path中。

    from 父级目录.folder_name import module
    这样就可以通过上述方式引用调用模块。