# Learning-CasRel **Repository Path**: yangkeloff/learning-cas-rel ## Basic Information - **Project Name**: Learning-CasRel - **Description**: Pytorch Bert_CasRel_RE 实体关系抽取项目 参考文献《A Novel Cascade Binary Tagging Framework for Relational Triple Extraction》 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-08-09 - **Last Updated**: 2023-08-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 流程 ## 1.生成关系表 > 02_process 提取duie/duie_schema.json中的`predicate`生成{index: relation}格式的关系字典 ## 2.加载关系表 > utils.py中get_rel() 使用`pd.read_csv('file_name.csv', names=["head1", ...])`通过names手动指定、或者生成表头 ``` # 返回值为 (['relation1', 'relation2', ...], {relation1: id1, ...}) ``` ## 3.Dataset初始化 > utils.py中class Dataset 根据不同场景加载不同数据文件 重点是加载BERT分词模型并在`__getitem__`中对每行数据进行分词,将BERT分词得到的`input_ids`, `offset_mapping`加入该行数据的dict中 `__getitem__`返回的数据为`parse_json()`处理后的 ## 4.计算实体位置 > class Dataset中parse_json() 对每条数据进行处理,目标格式如下 ``` { 'text': '...', 'input_ids': [233, ...], 'offset_mapping': [(0, 0), ...] 'sub_head_ids': [1, ...], 'sub_tail_ids': [2, ...], 'triple_list': [('subject', 'predicate', 'object'), ('subject', 'predicate', 'object'), ...], 'triple_id_list': [([1,2], 2, [3,4]), ([1,2], 2, [3,4]), ...], # 首尾位置id } ``` 先定义一个dict, 将已有的text, input_ids, offset_mapping填入,其它需要计算的属性先用空list`[]`填充 原始数据中的三元组数据在`spo_list`中,遍历取值以`(sub, rel, obj)`的形式append之前定义dict的`triple_id_list`中 实体位置不是在原始text中寻找,而是在BERT编码的input_ids中找 ## 5.Dataloader collate_fn批量加载数据 - 输入模型进行运算的数据,不能是 id,而应该是一个序列。所以借助DataLoader的collate_fn中间函数实现id转序列的过程。 - 用`torch.nn.utils.rnn.pad_sequence`写法上会相对简洁,但理解难度比较大,还容易出错,所以用最直接的循环遍历的方式处理 - 得到最大句子长度`max_len`,用`max_len - item_len`即得到填充长度`pad_len`,然后补0并更新mask ``` input_ids += [0] * pad_len mask = [1] * item_len + [0] * pad_len ``` - 对subject的头尾ids进行one-hot处理 - 随机选一个subject,同上操作 - 根据随机subject计算relation矩阵 在模型结构图中,关系矩阵维度为$relation\_size \times max\_len$; 而在预测时,句子中的每个字都有对应的relation($1 \times relation\_size$); ∴此处建立的rel矩阵为$max\_len \times relation\_size$ - 在数据的三元组中找到rel_id和obj的起始、结束位置,将矩阵的对应位置填1 ## 6.定义模型 模型分为**2**个部分:low level tagging module预测sub实体首尾位置、high level tagging module预测关系-obj实体矩阵 ### 模型初始化 - BERT冻结参数,只训练下游模型 - 定义4个`nn.linear`层,分别为sub、obj的首尾 - sub首尾为`sequence`,输出size为1 - obj首尾为`obj-rel`矩阵,输出size为REL_size ### 具体的计算方法 根据参数中的subject位置,找到对应词向量,然后加到句子的每个词上,再接一个全连接层,作为输出结果 ### 三维矩阵乘法条件 1. 两个矩阵的**后2个**维度构成的二维矩阵之间必须满足二维矩阵相乘的条件,即第一个矩阵的列数等于第二个矩阵的行数 2. 两个矩阵的**第0维分量数必须相等**(每个分量对应相乘) 或 有一方为1(broadcast-广播机制) ### 损失函数 - 交叉熵损失需要传入的预测值与真实值维度相同,最后一维如果可压缩使用`squeeze(-1)` - reduction参数用于控制损失函数计算结果的输出方式 - none: 输出每个样本的损失值 - sum: 对所有样本的损失值求和 - mean: 对所有样本的损失值求平均 - mask记录了填充位置为0,$loss \times mask$可以消除填充位loss对整体的影响 - sum(mask)即整个句子真实的长度 - 得到的loss值可以通过乘以参数进行增降权 ## 7.评估 - `torch.where()`返回的是一个tuple, 其中[0]号元素才是所需要的ids - 首尾id必须在mask为1的范围内 - $F_1 = \frac {2PR} {P + R}$ - 定义EPS防止分母为0 - `unsqueeze(0)`在0维扩展一维(增加一个维度) - 之前由于要遍历句子中的每一个字,所以obj-rel矩阵为$max\_len \times relation\_size$,现在要基于rel找obj的首尾,∴对obj-rel矩阵进行转置为$relation\_size \times max\_len$,以遍历rel ### 步骤 1. 解析sub内容,首尾转化成one-hot 2. 遍历所有rel,找满足条件的obj 3. 返回set去重后的[(sub, rel, obj), ...] ## 8.预测 - 将`encoding`设置为`utf-8`,以支持中文字符 - `ensure_ascii`参数被设置为False,在保存JSON文件时,中文字符不会被转义成Unicode转义序列