当前位置: 首页 > news >正文

Casrel关系抽取

Joint联合抽取方法-Casrel模型


1 Joint方法的原理

joint联合抽取方法是通过修改标注方法和模型结构直接输出文本中包含的(spo)三元组. Joint联合抽取方法又分为: “参数共享的联合模型” 和 “联合解码的联合模型”:


  • 参数共享的联合模型:

在这里插入图片描述

  • 主体、客体和关系的抽取不是同步的 (一般情况,但是也可以其中两个任务一起进行) ,各个过程都可以得到一个loss值,整个模型的loss是各过程loss值之和.

  • 联合解码的联合模型:

在这里插入图片描述

  • 主体、客体和关系的抽取是同步的,通过一个模型直接得到SPO三元组. 如: 采用序列标注形式实现模型训练,如果有N种关系,对于每种关系,将其与BIOS以及主实体和客实体的序号 (1,2) 组合起来进行关系抽取,并根据最后的标注结果进行解码,一共涉及 2×3×N+1​个标签 (其中1代表不属于任何一种关系) .

2 Casrel算法思想

  • 该模型主要解决的问题为关系三元组重叠问题.
  • CasRel 本质上是基于参数共享的联合实体关系抽取方法.

3 Casrel模型架构

在这里插入图片描述


  • CASREL框架抽取三元组(subject, relation, object)主要包含两个步骤,三个部分

  • 两个步骤:
    • 第一步要识别出句子中的 subject .
    • 第二步要根据识别出的 subject, 识别出所有有可能的 relation 以及对应的 object.

  • 三个部分:

    • 编码器部分: 可以替换为不同的编码框架,主要对句子中的词进行编码,论文最终以BERT为主,效果不错.
    • 解码器—-头实体识别部分:目的是识别出句子中的 subject.
    • 解码器—-关系与尾实体联合识别部分:根据 subject,寻找可能的 relation 和 object.
  • 由上述模型结果得出结论:Casrel模型可以解决最开始提到关系抽取中的 SEO 和 EPO 的重叠问题. 这也是我们本次项目研究问题的重点,解决文本中多元关系问题.


4 项目代码实现

5 数据预处理

  • 本项目中对数据部分的预处理步骤如下:
    • 第一步: 查看项目数据集
    • 第二步: 编写Config类项目文件配置代码
    • 第三步: 编写数据处理相关函数
    • 第四步: 构建DataSet类与dataloader函数

第一步: 查看项目数据集


  • 项目数据的路径为:/home/ec2-user/Casrel_RE/relationship_extract/data
  • 项目的数据集包括6个json文件(其中predict_spo.json是模型预测结果,rel_type.json是带有实体类型的关系文件), 我们这里只先针对train.json、dev.json、test.json、relation.json进行介绍:
  • 关系类型文件: /home/ec2-user/Casrel_RE/relationship_extract/data/relation.json

{"0": "出品公司","1": "国籍","2": "出生地","3": "民族","4": "出生日期","5": "毕业院校","6": "歌手","7": "所属专辑","8": "作词","9": "作曲","10": "连载网站","11": "作者","12": "出版社","13": "主演","14": "导演","15": "编剧","16": "上映时间","17": "成立日期"
}

  • rel.json中包含18个类别标签, json文件可以看作是一个字典,key对应关系的id,value对应关系类型.
  • 训练数据集: /home/ec2-user/Casrel_RE/relationship_extract/data/train.json

{"text": "笔       名:木斧原       名:杨莆曾  用  名:穆新文、牧羊、寒白、洋漾出生日期:1931—职       业:作家、诗人性    别: 男民    族: 回族政治面貌:中共党员 祖       籍:固原县出  生  地:成都", "spo_list": [{"predicate": "民族", "object_type": "文本", "subject_type": "人物", "object": "回族", "subject": "木斧"}, {"predicate": "出生日期", "object_type": "日期", "subject_type": "人物", "object": "1931", "subject": "木斧"}, {"predicate": "出生地", "object_type": "地点", "subject_type": "人物", "object": "成都", "subject": "木斧"}]}

train.json中包含55433行样本, 每行为一个字典样式, 第一个key为"text", 对应的value为待抽取关系的中文文本, 第二个key为"spo_list", 对应的value为句子中真实的spo关系三元组列表 (列表中含有多个spo三元组)

以spo_list的其中一个元素为例:元素格式为字典,其中"predictate"代表为关系类型; "object_type"代表尾实体的类型; "subject_type"代表主实体的类型; "object"代表尾实体; “subject” 代表主实体.


  • 验证数据集: /home/ec2-user/Casrel_RE/relationship_extract/data/dev.json

{"text": "蔡志坚在南京艺术学院求学时受过系统、正规的艺术教育和专业训练,深得刘海粟、罗叔子、陈之佛、谢海燕、陈大羽等著名中国画大师的指授,基本功扎实,加上他坚持从生活中汲取创作源泉,用心捕捉生活中最美最感人的瞬间形象,因而他的作品,不论是山水、花鸟、飞禽、走兽,无不充满了生命的灵气,寄托着画家的情怀,颇得自然之真趣", "spo_list": [{"predicate": "毕业院校", "object_type": "学校", "subject_type": "人物", "object": "南京艺术学院", "subject": "蔡志坚"}]}

dev.json中包含11191行样本, 每行为一个字典样式, 第一个key为"text", 对应的value为待抽取关系的中文文本, 第二个key为"spo_list", 对应的value为句子中真实的spo关系三元组列表 (列表中含有多个spo三元组)

以spo_list的其中一个元素为例:元素格式为字典,其中"predictate"代表为关系类型; "object_type"代表尾实体的类型; "subject_type"代表主实体的类型; "object"代表尾实体; “subject” 代表主实体.


  • 测试数据集: /home/ec2-user/Casrel_RE/relationship_extract/data/test.json

{"text": "1997年,李柏光从北京大学法律系博士毕业", "spo_list": [{"predicate": "毕业院校", "object_type": "学校", "subject_type": "人物", "object": "北京大学", "subject": "李柏光"}]}

test.json中包含13417行样本, 每行为一个字典样式, 第一个key为"text", 对应的value为待抽取关系的中文文本, 第二个key为"spo_list", 对应的value为句子中真实的spo关系三元组列表 (列表中含有多个spo三元组)

以spo_list的其中一个元素为例:元素格式为字典,其中"predictate"代表为关系类型; "object_type"代表尾实体的类型; "subject_type"代表主实体的类型; "object"代表尾实体; “subject” 代表主实体.



第二步:编写Config类项目文件配置代码

  • Config类文件路径为: /home/ec2-user/Casrel_RE/relationship_extract/codes/config.py

  • config文件目的:配置项目常用变量,一般这些变量属于不经常改变的,比如:训练文件路径、模型训练次数、模型超参数等等
# 导入必备的工具包
import torch
# 导入Vocabulary,目的:用于构建, 存储和使用 `str` 到 `int` 的一一映射
from fastNLP import Vocabulary
from transformers import BertTokenizer, AdamW
import json# 构建配置文件Config类
class Config(object):def __init__(self):# 设置是否使用GPU来进行模型训练self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')self.bert_path = "预训练模型的绝对路径"self.num_rel = 18  # 关系的种类数self.batch_size = 8self.train_data_path = "训练数据集的绝对路径"self.dev_data_path = "验证数据集的绝对路径"self.test_data_path = "测试数据集的绝对路径"self.rel_dict_path = "关系数据文件的绝对路径"id2rel = json.load(open(self.rel_dict_path, encoding='utf8'))self.rel_vocab = Vocabulary(padding=None, unknown=None)# vocab更新自己的字典,输入为list列表self.rel_vocab.add_word_lst(list(id2rel.values()))self.tokenizer = BertTokenizer.from_pretrained(self.bert_path)self.learning_rate = 1e-5self.bert_dim = 768self.epochs = 10

第三步: 编写数据处理相关函数

  • 函数代码路径: /home/ec2-user/Casrel_RE/relationship_extract/codes/utils/process.py

  • 首选导入必备的工具包
# coding:utf-8
from codes.config import *
import torch
from random import choice
from collections import defaultdictconf = Config()

  • 构建第一个数据处理相关函数find_head_idx, 位于process.py中的独立函数.
def find_head_idx(source, target):# # 获取实体的开始索引位置target_len = len(target)for i in range(len(source)):if source[i: i + target_len] == target:return ireturn -1

  • 构建第二个数据处理相关函数create_label, 位于process.py中的独立函数.
def create_label(inner_triples, inner_input_ids, seq_len):# 获取每个样本的:主实体长度、主实体开始和结束位置张量表示、客实体以及对应关系实现张量表示inner_sub_heads, inner_sub_tails = torch.zeros(seq_len), torch.zeros(seq_len)inner_obj_heads = torch.zeros((seq_len, conf.num_rel))inner_obj_tails = torch.zeros((seq_len, conf.num_rel))inner_sub_head2tail = torch.zeros(seq_len)  # 随机抽取一个实体,从开头一个词到末尾词的索引# 因为数据预处理代码还待优化,会有不存在关系三元组的情况,# 初始化一个主词的长度为1,即没有主词默认主词长度为1,# 防止零除报错,初始化任何非零数字都可以,没有主词分子是全零矩阵inner_sub_len = torch.tensor([1], dtype=torch.float)# 主词到谓词的映射s2ro_map = defaultdict(list)# print(s2ro_map)for inner_triple in inner_triples:# print(inner_triple)inner_triple = (conf.tokenizer(inner_triple['subject'], add_special_tokens=False)['input_ids'],conf.rel_vocab.to_index(inner_triple['predicate']),conf.tokenizer(inner_triple['object'], add_special_tokens=False)['input_ids'])sub_head_idx = find_head_idx(inner_input_ids, inner_triple[0])obj_head_idx = find_head_idx(inner_input_ids, inner_triple[2])if sub_head_idx != -1 and obj_head_idx != -1:sub = (sub_head_idx, sub_head_idx + len(inner_triple[0]) - 1)# s2ro_map保存主语到谓语的映射s2ro_map[sub].append((obj_head_idx, obj_head_idx + len(inner_triple[2]) - 1, inner_triple[1]))  # {(3,5):[(7,8,0)]} 0是关系if s2ro_map:for s in s2ro_map:inner_sub_heads[s[0]] = 1inner_sub_tails[s[1]] = 1sub_head_idx, sub_tail_idx = choice(list(s2ro_map.keys()))inner_sub_head2tail[sub_head_idx:sub_tail_idx + 1] = 1inner_sub_len = torch.tensor([sub_tail_idx + 1 - sub_head_idx], dtype=torch.float)for ro in s2ro_map.get((sub_head_idx, sub_tail_idx), []):inner_obj_heads[ro[0]][ro[2]] = 1inner_obj_tails[ro[1]][ro[2]] = 1return inner_sub_len, inner_sub_head2tail, inner_sub_heads, inner_sub_tails, inner_obj_heads, inner_obj_tails

  • 构建第三个数据处理相关函数collate_fn, 位于process.py中的独立函数.
def collate_fn(data):text_list = [value[0] for value in data]triple = [value[1] for value in data]# 按照batch中最长句子补齐text = conf.tokenizer.batch_encode_plus(text_list,padding=True)batch_size = len(text['input_ids'])seq_len = len(text['input_ids'][0])sub_heads = []sub_tails = []obj_heads = []obj_tails = []sub_len = []sub_head2tail = []# 循环遍历每个样本,将实体信息进行张量的转化for batch_index in range(batch_size):inner_input_ids = text['input_ids'][batch_index]  # 单个句子变成索引后inner_triples = triple[batch_index]# 获取每个样本的:主实体长度、主实体开始和结束位置张量表示、客实体以及对应关系实现张量表示results = create_label(inner_triples, inner_input_ids, seq_len)sub_len.append(results[0])sub_head2tail.append(results[1])sub_heads.append(results[2])sub_tails.append(results[3])obj_heads.append(results[4])obj_tails.append(results[5])input_ids = torch.tensor(text['input_ids']).to(conf.device)mask = torch.tensor(text['attention_mask']).to(conf.device)# 借助torch.stack()函数沿一个新维度对输入batch_size张量序列进行连接,序列中所有张量应为相同形状;stack 函数返回的结果会新增一个维度,sub_heads = torch.stack(sub_heads).to(conf.device)sub_tails = torch.stack(sub_tails).to(conf.device)sub_len = torch.stack(sub_len).to(conf.device)sub_head2tail = torch.stack(sub_head2tail).to(conf.device)obj_heads = torch.stack(obj_heads).to(conf.device)obj_tails = torch.stack(obj_tails).to(conf.device)inputs = {'input_ids': input_ids,'mask': mask,'sub_head2tail': sub_head2tail,'sub_len': sub_len}labels = {'sub_heads': sub_heads,'sub_tails': sub_tails,'obj_heads': obj_heads,'obj_tails': obj_tails}return inputs, labels

第四步:构建DataSet类以及Dataloader函数

  • 代码路径为: /home/ec2-user/Casrel_RE/relationship_extract/codes/utils/data_loader.py

  • 首先导入相应的工具包
# coding:utf-8
from torch.utils.data import DataLoader, Dataset
from utils.process import *
conf = Config()

  • 构建第一个数据处理相关类MyDataset, 位于data_loader.py中的独立类.
# 自定义Dataset
class MyDataset(Dataset):def __init__(self, data_path):super(MyDataset, self).__init__()self.dataset = [json.loads(line) for line in open(data_path, encoding='utf8')]def __len__(self):return len(self.dataset)def __getitem__(self, index):content = self.dataset[index]text = content['text']spo_list = content['spo_list']return text, spo_list

  • 构建第二个数据处理相关函数get_data, 位于data_loader.py中的独立函数.
def get_data():# 实例化训练数据集Dataset对象train_data = MyDataset(conf.train_data_path)# 实例化验证数据集Dataset对象dev_data = MyDataset(conf.dev_data_path)# 实例化测试数据集Dataset对象test_data = MyDataset(conf.test_data_path)# 实例化训练数据集Dataloader对象train_dataloader = DataLoader(dataset=train_data,batch_size=conf.batch_size,shuffle=True,collate_fn=collate_fn,drop_last=True)# 实例化验证数据集Dataloader对象dev_dataloader = DataLoader(dataset=dev_data,batch_size=conf.batch_size,shuffle=True,collate_fn=collate_fn,drop_last=True)# 实例化测试数据集Dataloader对象test_dataloader = DataLoader(dataset=test_data,batch_size=conf.batch_size,shuffle=True,collate_fn=collate_fn,drop_last=True)return train_dataloader, dev_dataloader, test_dataloader

6 Casrel模型搭建

  • 本项目中Casrel模型搭建的步骤如下:
    • 第一步: 编写模型类的代码
    • 第二步: 编写工具类函数,训练函数,验证函数,测试函数
    • 第三步: 编写使用模型预测代码的实现.

第一步: 编写模型类的代码

  • 文本编码采用的是BERT预训练模型

  • 第一步: 实现CasRel类代码.
  • 代码路径: /home/ec2-user/Casrel_RE/relationship_extract/codes/model/CasrelModel.py
# coding:utf-8
import torch
import torch.nn as nn
from transformers import BertModel, AdamW
from codes.config import *class CasRel(nn.Module):def __init__(self, conf):super().__init__()self.bert = BertModel.from_pretrained(conf.bert_path)# 定义第一个线性层,来判断主实体的头部位置self.sub_heads_linear = nn.Linear(conf.bert_dim, 1)# 定义第二个线性层,来判断主实体的尾部位置self.sub_tails_linear = nn.Linear(conf.bert_dim, 1)# 定义第三个线性层,来判断客实体的头部位置以及关系类型self.obj_heads_linear = nn.Linear(conf.bert_dim, conf.num_rel)# 定义第四个线性层,来判断客实体的尾部位置以及关系类型self.obj_tails_linear = nn.Linear(conf.bert_dim, conf.num_rel)def get_encoded_text(self, token_ids, mask):encoded_text = self.bert(token_ids, attention_mask=mask)[0]return encoded_textdef get_subs(self, encoded_text):pre_sub_heads = torch.sigmoid(self.sub_heads_linear(encoded_text))pre_sub_tails = torch.sigmoid(self.sub_tails_linear(encoded_text))return pre_sub_heads, pre_sub_tailsdef get_objs_for_specific_sub(self, sub_head2tail, sub_len, encoded_text):'''将subject实体信息融合原始句子中:将主实体字向量实现平均,然后加在当前句子的每一个字向量上,进行计算:param sub_head2tail:shape-->【16,1, 200】:param sub_len:shape--->[16,1]:param encoded_text:.shape[16,200,768]:return:pred_obj_heads-->shape []pre_obj_tails-->shape []'''sub = torch.matmul(sub_head2tail, encoded_text)# 将主实体特征和编码后的文本进行融合sub_len = sub_len.unsqueeze(1) # 主实体长度(扩维)sub = sub / sub_len # 平均主实体信息encoded_text = encoded_text + sub #将处理后的实体特征和原始编码后的文本进行融合pred_obj_heads = torch.sigmoid(self.obj_heads_linear(encoded_text))pre_obj_tails = torch.sigmoid(self.obj_tails_linear(encoded_text))return pred_obj_heads, pre_obj_tailsdef forward(self, input_ids, mask, sub_head2tail, sub_len):''':param input_ids: shape-->[16, 200]:param mask: shape-->[16, 200]:param sub_head2tail: shape-->[16, 200]:param sub_len: shape-->[16, 1]:return:'''# todo: encode_text.shape--->[16,200,768]encoded_text = self.get_encoded_text(input_ids, mask)pred_sub_heads, pre_sub_tails = self.get_subs(encoded_text)sub_head2tail = sub_head2tail.unsqueeze(1)pred_obj_heads, pre_obj_tails =self.get_objs_for_specific_sub(sub_head2tail, sub_len,encoded_text)result_dict = {'pred_sub_heads': pred_sub_heads,'pred_sub_tails': pre_sub_tails,'pred_obj_heads': pred_obj_heads,'pred_obj_tails': pre_obj_tails,'mask': mask}return result_dictdef compute_loss(self,pred_sub_heads, pred_sub_tails,pred_obj_heads, pred_obj_tails,mask,sub_heads, sub_tails,obj_heads, obj_tails):'''计算损失:param pred_sub_heads:[16, 200, 1]:param pred_sub_tails:[16, 200, 1]:param pred_obj_heads:[16, 200, 18]:param pred_obj_tails:[16, 200, 18]:param mask: shape-->[16, 200]:param sub_heads: shape-->[16, 200]:param sub_tails: shape-->[16, 200]:param obj_heads: shape-->[16, 200, 18]:param obj_tails: shape-->[16, 200, 18]:return:'''# todo:sub_heads.shape,sub_tails.shape, mask-->[16, 200]# todo:obj_heads.shape,obj_tails.shape-->[16, 200, 18]rel_count = obj_heads.shape[-1]rel_mask = mask.unsqueeze(-1).repeat(1, 1, rel_count)loss_1 = self.loss(pred_sub_heads, sub_heads, mask)loss_2 = self.loss(pred_sub_tails, sub_tails, mask)loss_3 = self.loss(pred_obj_heads, obj_heads, rel_mask)loss_4 = self.loss(pred_obj_tails, obj_tails, rel_mask)return loss_1 + loss_2 + loss_3 + loss_4def loss(self, pred, gold, mask):pred = pred.squeeze(-1)los = nn.BCELoss(reduction='none')(pred, gold)if los.shape != mask.shape:mask = mask.unsqueeze(-1)los = torch.sum(los * mask) / torch.sum(mask)return losdef load_model(conf):device = conf.devicemodel = CasRel(conf)model.to(device)# 因为本次模型借助BERT做fine_tuning, 因此需要对模型中的大部分参数进行L2正则处理防止过拟合,包括权重w和偏置b# prepare optimzier# named_parameters()获取模型中的参数和参数名字param_optimizer = list(model.named_parameters())print(f'param_optimizer--->{param_optimizer}')no_decay = ["bias", "LayerNorm.bias", "LayerNorm.weight"]  # no_decay中存放不进行权重衰减的参数{因为bert官方代码对这三项免于正则化}# any()函数用于判断给定的可迭代参数iterable是否全部为False,则返回False,如果有一个为True,则返回True# 判断param_optimizer中所有的参数。如果不在no_decay中,则进行权重衰减;如果在no_decay中,则不进行权重衰减optimizer_grouped_parameters = [{"params": [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], "weight_decay": 0.01},{"params": [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], "weight_decay": 0.0}]optimizer = AdamW(optimizer_grouped_parameters, lr=conf.learning_rate, eps=10e-8)# 是否需要对bert进行warm_up。这里默认不进行sheduler = Nonereturn model, optimizer, sheduler, deviceif __name__ == '__main__':conf = Config()# casrel = CasRel(conf)# print(f'模型的架构--->{casrel}')load_model(conf)

第二步: 编写工具类函数,训练函数,验证函数,测试函数

  • 注意:工具类函数需要在训练、测试、评估过程中使用;训练函数, 验证函数两者在一个脚本,测试函数单独一个脚本. 此外, 因为验证函数和测试函数一致,因此只写一个即可.

  • 第一步: 实现utils函数.
  • 代码路径: /home/ec2-user/Casrel_RE/relationship_extract/codes/utils/process.py
# coding:utf-8
from codes.config import *
import torch
from random import choice
from collections import defaultdictconf = Config()def extract_sub(pred_sub_heads, pred_sub_tails):''':param pred_sub_heads: 模型预测出的主实体开头位置:param pred_sub_tails: 模型预测出的主实体尾部位置:return: subs列表里面对应的所有实体【head, tail】'''subs = []# 统计预测出所有值为1的元素索引位置heads = torch.arange(0, len(pred_sub_heads), device=conf.device)[pred_sub_heads == 1]tails = torch.arange(0, len(pred_sub_tails), device=conf.device)[pred_sub_tails == 1]for head, tail in zip(heads, tails):if tail >= head:subs.append((head.item(), tail.item()))return subsdef extract_obj_and_rel(obj_heads, obj_tails):''':param obj_heads:  模型预测出的从实体开头位置以及关系类型:param obj_tails:  模型预测出的从实体尾部位置以及关系类型:return: obj_and_rels:元素形状:(rel_index, start_index, end_index)'''obj_heads = obj_heads.Tobj_tails = obj_tails.Trel_count = obj_heads.shape[0]obj_and_rels = []for rel_index in range(rel_count):obj_head = obj_heads[rel_index]obj_tail = obj_tails[rel_index]objs = extract_sub(obj_head, obj_tail)if objs:for obj in objs:start_index, end_index = objobj_and_rels.append((rel_index, start_index, end_index))return obj_and_relsdef convert_score_to_zero_one(tensor):'''以0.5为阈值,大于0.5的设置为1,小于0.5的设置为0'''tensor[tensor >= 0.5] = 1tensor[tensor < 0.5] = 0return tensor

  • 导入实现训练函数,验证函数,测试函数的工具包
# coding:utf-8from model.CasrelModel import *
from utils.process import *
from utils.data_loader import *
from config import *
import pandas as pd
from tqdm import tqdm

  • 第二步: 编写训练函数.
  • 思想
1.遍历训练数据送入模型
2.模型得到预测结果
3.分别计算4个loss
4.梯度清零
5.反向传播
6.梯度更新
7.保存模型

  • 第三步: 编写验证函数.
  • 思想
1.遍历验证数据送入模型
2.模型得到预测结果(主实体开始位置和结束位置分数;客实体开始位置和关系以及结束位置和关系的分数)
3.将分数概率值,以0.5为阈值,大于等于0.5的值设置为1,否则为0
4.分别抽取出主实体开始和结束位置,以及抽取出客实体开始和结束及对应的关系
5.计算指标:precision、recall、 f1值

  • 第四步: 编写测试函数.
    • 代码路径:/home/ec2-user/Casrel_RE/relationship_extract/codes/test.py
    • 代码过程和思想和预测函数一样

第三步: 编写模型预测函数

  • 使用训练好的模型,随机抽取文本进行关系抽取
  • 代码位置: /home/ec2-user/Casrel_RE/relationship_extract/codes/predict.py
基本步骤:
1. 基于原始的文本,进行张量的转换
2. 基于张量输入,送入模型得到模型预测的主实体
3. 选择其中的一个主实体,获取其sub_head2tail,以及sub_len
4. 基于第3步选择的主实体,去预测对应的客实体和关系

7 Joint联合抽取方法的优缺点

  • 优点:
    • 两个任务的表征有交互作用可能辅助任务的学习.
    • 不用训练多个模型,一个模型解决问题,减少训练与预测的gap.
  • 缺点:
    • 更复杂的模型结构.
    • Joint方法提取的特征可能一致,也可能冲突会使模型学习变得混乱.

http://www.lryc.cn/news/613542.html

相关文章:

  • vue3 el-select 加载触发
  • AI绘画:生成唐初李世民全身像提示词
  • 【unity实战】使用Unity程序化生成3D随机地牢(附项目源码)
  • 8.3.1 注册服务中心Etcd
  • 【感知机】感知机(perceptron)学习算法的对偶形式
  • Java包装类详解与应用指南
  • Caffeine 三种过期策略详解
  • Day 6: CNN卷积神经网络 - 计算机视觉的核心引擎
  • MCU中的USB
  • 论文解读:单个标点符号如何欺骗LLM,攻破AI评判系统
  • Linux总线,设备和驱动关系以及匹配机制解析
  • vue打包号的文件如何快速查找文件打包后的位置
  • Redis 编译错误:缺少静态库文件,如何解决?
  • 在NVIDIA Orin上用TensorRT对YOLO12进行多路加速并行推理时内存泄漏 (中)
  • PoE延长器——突破网络距离限制
  • 数据赋能(386)——数据挖掘——迭代过程
  • PyCharm 图标 c、m、f、F、v、p 的含义
  • 科技云报到:热链路革命:阿卡 CRM 的 GTM 定位突围
  • 健永科技工位RFID读卡器实现生产流水线物料跟踪与柔性化升级
  • 美食广场: 城市胃的便利店
  • MySQL UNION 操作符详细说明
  • 如何在GPU上安装使用Docker
  • SupChains团队:订单生产型供应链销量预测建模案例分享(六)
  • 容器之王--Docker的部署及基本操作演练
  • vLLM:彻底改变大型语言模型推理延迟和吞吐量
  • Aurora MySQL 8.0 性能分析账号创建完整指南
  • 神经网络入门指南:从零理解 PyTorch 的核心思想
  • 跨境电商增长突围:多维变局下的战略重构与技术赋能
  • 从“数字网格”到“空中交警” :星图低空云如何重构低空管理?
  • 鸿蒙 - 分享功能