【专辑】AI大模型应用开发入门-拥抱Hugging Face与Transformers生态 - 基于BERT文本分类模型微调

大家好,我是java1234_小锋老师,最近更新《AI大模型应用开发入门-拥抱Hugging Face与Transformers生态》专辑,感谢大家支持。

本课程主要介绍和讲解Hugging Face和Transformers,包括加载预训练模型,自定义数据集,模型推理,模型微调,模型性能评估等。是AI大模型应用开发的入门必备知识。

基于BERT文本分类模型微调

模型微调简介

模型微调(Model Fine-Tuning) 是迁移学习中的一种核心技术,指在一个已经预训练好的大型通用模型(称为基座模型)的基础上,使用特定领域或任务的数据集进行额外的、有针对性的训练,使模型适应新任务的过程。

通俗比喻:

  • 预训练:让一个学生(模型)读完整个图书馆的书,获得广泛的通识知识。

  • 微调:然后让这个学生专攻某一个学科(例如医学或法律),学习这个领域的专业书籍和案例,从而成为该领域的专家。

为什么需要微调?(优势)

  1. 降低数据需求:从头训练一个大模型(如LLM或CV大模型)需要海量数据和巨大的算力。微调只需相对少量的高质量专业数据即可,成本极低。

  2. 节省计算资源和时间:预训练模型已经学会了通用的特征表示(如语言规则、图像纹理),微调只需在此基础上“微调”参数,训练速度快得多。

  3. 提升特定任务性能:在目标领域(如医疗问答、法律文书分析、特定风格的绘画)上,微调后的模型性能远优于直接使用通用模型。

  4. 避免灾难性遗忘:与从头学习相比,微调是一种温和的调整,能在保持模型通用能力的同时,增强其专业能力。

基于BERT模型微调训练

1,自定义数据集

首先我们准备下训练和测试数据。

8千多条的训练集数据,2千多条的测试集数据。

我们之所以要自定义数据集,是因为需要去适配训练模型需要的数据格式。

自定义数据集参考代码:

from datasets import load_dataset from torch.utils.data import Dataset # 自定义数据集 class MyDataset(Dataset): def __init__(self, split): # 加载训练集 train_dataset = load_dataset(path="csv", data_files="../weibo_senti_8k_train.csv") # 加载测试集 test_dataset = load_dataset(path="csv", data_files="../weibo_senti_2k_test.csv") if split == 'train': self.data = train_dataset['train'] elif split == 'test': self.data = test_dataset['train'] # 获取数据集大小 def __len__(self): return len(self.data) # 获取数据集的某个元素 def __getitem__(self, index): sentence = self.data[index]['sentence'] label = self.data[index]['label'] return sentence, label if __name__ == '__main__': train_dataset = MyDataset('train') test_dataset = MyDataset('test') print(train_dataset[0]) print(test_dataset[0])

运行结果:

2,对训练输入文本进行编码

对传入的数据进行训练之前,我们需要对数据进行编码。

我们通过分词器的batch_encode_plus方法进行批量编码;

实例代码:

from transformers import BertTokenizer # 加载分词器 tokenizer = BertTokenizer.from_pretrained('../Bert-base-chinese') # 准备测试文本 sents = ['床前明月光,疑似地上霜,举头望明月,低头思故乡', '今天天气不错', '很开心'] # 批量编码句子 out = tokenizer.batch_encode_plus( batch_text_or_text_pairs=sents, # 输入的文本 add_special_tokens=True, # 添加特殊标记 max_length=10, # 最大长度 padding='max_length', # 填充 truncation=True, # 截断 return_tensors='pt', # 返回pytorch张量 return_token_type_ids=True, # 返回token_type_ids 区分不同句子或段落的类型标识 return_attention_mask=True, # 返回attention_mask 标记有效token位置的掩码 return_special_tokens_mask=True # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码 ) print(out) for k, v in out.items(): print(k, v) # 解码文本数据 for i in range(len(sents)): print(sents[i] + "--编码后:", tokenizer.decode(out['input_ids'][i]))

运行输出:

{'input_ids': tensor([[ 101, 2414, 1184, 3209, 3299, 1045, 8024, 4542, 849, 102], [ 101, 791, 1921, 1921, 3698, 679, 7231, 102, 0, 0], [ 101, 2523, 2458, 2552, 102, 0, 0, 0, 0, 0]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'special_tokens_mask': tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 1, 1, 1], [1, 0, 0, 0, 1, 1, 1, 1, 1, 1]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]])} input_ids tensor([[ 101, 2414, 1184, 3209, 3299, 1045, 8024, 4542, 849, 102], [ 101, 791, 1921, 1921, 3698, 679, 7231, 102, 0, 0], [ 101, 2523, 2458, 2552, 102, 0, 0, 0, 0, 0]]) token_type_ids tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) special_tokens_mask tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 1, 1, 1], [1, 0, 0, 0, 1, 1, 1, 1, 1, 1]]) attention_mask tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]]) 床前明月光,疑似地上霜,举头望明月,低头思故乡--编码后: [CLS] 床 前 明 月 光 , 疑 似 [SEP] 今天天气不错--编码后: [CLS] 今 天 天 气 不 错 [SEP] [PAD] [PAD] 很开心--编码后: [CLS] 很 开 心 [SEP] [PAD] [PAD] [PAD] [PAD] [PAD]
3,定义增量模型,也就是下游任务

我们定义增量模型,主要是在Bert模型最后加一个全连接层,实现二分类任务。进行前向传播的时候,我们要使用torch.no_grad()冻结Bert模型参数,不需计算梯度,获取最后一层Bert隐藏层输出,调用自定义的全连接层,进行增量模型训练。

示例代码:

# 使用设备(GPU/CPU) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(device) # 加载预训练模型 pretrained_model = BertModel.from_pretrained('./Bert-base-chinese').to(device) # 定义下游任务(增量模型) class DownStreamModel(torch.nn.Module): def __init__(self): super(DownStreamModel, self).__init__() # 下游加一个全连接层,实现二分类任务 self.fc = torch.nn.Linear(768, 2) def forward(self, input_ids, attention_mask, token_type_ids): with torch.no_grad(): # 冻结Bert模型参数,不需计算梯度 # 获取最后一层隐藏层输出 output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) # 增量模型参与训练 out = self.fc(output.last_hidden_state[:, 0, :]) return out

output.last_hidden_state[:, 0, :]是用来获取模型输出的隐藏状态(通常是进入模型的最后一层隐藏状态)的一个特定切片。让我们逐个参数进行详细解析:

1.output

output通常是通过对输入数据(如文本、图像等)运行某个深度学习模型(例如 BERT、GPT、Transformers 等)后返回的对象,它包含多个属性,其中一个重要的属性是last_hidden_state

2.last_hidden_state

last_hidden_state是模型最后一层的隐藏状态,它的形状通常是(batch_size, sequence_length, hidden_size),具体的含义如下:

  • batch_size:处理的样本数量。在一次前向传播中,模型可以并行处理多个输入样本。

  • sequence_length:输入序列的长度。对于处理文本来说,它表示词汇的数量,可以是句子中词的个数。

  • hidden_size:每个隐藏状态的维度,代表模型的输出特征维数。通常在模型架构中定义,例如对于 BERT-base 隐藏大小为 768。

3.[:, 0, :]

这里的切片操作用来访问last_hidden_state的一部分,具体解释如下:

  • ::表示选择所有的样本。由于batch_size是第一个维度,所以把:放在这个位置表示选取当前批次的所有样本。

  • 0:表示选择第一个时刻的隐藏状态。在 NLP 中,特别是像 BERT 这样的模型中,第一位置的隐藏状态通常对应于[CLS]token(分类 token),它是用来进行句子级别的任务(例如分类)的。

  • ::表示选择所有的特征维度(hidden_size)。这意味着我们提取每个样本的第一个位置(即[CLS]token)对应的全部隐藏状态特征。

综合

因此,output.last_hidden_state[:, 0, :]表示提取所有输入样本的[CLS]token 的隐藏状态向量,这通常用于下游任务,如文本分类、问答、情感分析等,因为[CLS]token 的表示通常是整段文本的语义聚合。

例子

如果有一个输入批次大小为 4,并且每个输入序列的隐藏状态大小为 768,那么output.last_hidden_state[:, 0, :]的返回结果将是一个形状为(4, 768)的张量,其中每一行对应一个输入样本的[CLS]token 的隐藏状态。

4,训练模型

前面我们已经定义好了数据集,以及文本编码处理,包括增量模型定义。接下来我们来进行增量模型微调训练。

实例代码:

import torch from datasets import load_dataset from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer, BertModel # 自定义数据集 class MyDataset(Dataset): def __init__(self, split): # 加载训练集 train_dataset = load_dataset(path="csv", data_files="./weibo_senti_8k_train.csv") # 加载测试集 test_dataset = load_dataset(path="csv", data_files="./weibo_senti_2k_test.csv") if split == 'train': self.data = train_dataset['train'] elif split == 'test': self.data = test_dataset['train'] # 获取数据集大小 def __len__(self): return len(self.data) # 获取数据集的某个元素 def __getitem__(self, index): sentence = self.data[index]['sentence'] label = self.data[index]['label'] return sentence, label # 加载分词器 tokenizer = BertTokenizer.from_pretrained('./Bert-base-chinese') # 使用设备(GPU/CPU) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(device) # 加载预训练模型 pretrained_model = BertModel.from_pretrained('./Bert-base-chinese').to(device) # 定义下游任务(增量模型) class DownStreamModel(torch.nn.Module): def __init__(self): super(DownStreamModel, self).__init__() # 下游加一个全连接层,实现二分类任务 self.fc = torch.nn.Linear(768, 2) def forward(self, input_ids, attention_mask, token_type_ids): with torch.no_grad(): # 冻结Bert模型参数,不需计算梯度 # 获取最后一层隐藏层输出 output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) # 增量模型参与训练 out = self.fc(output.last_hidden_state[:, 0, :]) return out # 对传入数据进行编码 def collate_fn(data): sents = [i[0] for i in data] labels = [i[1] for i in data] # 编码 # 批量编码句子 out = tokenizer.batch_encode_plus( batch_text_or_text_pairs=sents, # 输入的文本 add_special_tokens=True, # 添加特殊标记 max_length=256, # 最大长度 padding='max_length', # 填充 truncation=True, # 截断 return_tensors='pt', # 返回pytorch张量 return_token_type_ids=True, # 返回token_type_ids 区分不同句子或段落的类型标识 return_attention_mask=True, # 返回attention_mask 标记有效token位置的掩码 return_special_tokens_mask=True # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码 ) return out['input_ids'], out['attention_mask'], out['token_type_ids'], torch.tensor(labels) # 创建数据集 train_dataset = MyDataset('train') # 训练集 train_loader = DataLoader( dataset=train_dataset, # 数据集 batch_size=200, # 批次大小 shuffle=True, # 是否打乱数据 drop_last=True, # 丢弃最后一个批次数据 collate_fn=collate_fn # 对加载的数据进行编码 ) test_dataset = MyDataset('test') # 测试集 test_loader = DataLoader( dataset=test_dataset, # 数据集 batch_size=200, # 批次大小 shuffle=True, # 是否打乱数据 drop_last=True, # 丢弃最后一个批次数据 collate_fn=collate_fn # 对加载的数据进行编码 ) if __name__ == '__main__': model = DownStreamModel().to(device) # 创建模型 optimizer = torch.optim.AdamW(model.parameters()) # 优化器 criterion = torch.nn.CrossEntropyLoss() # 定义损失函数 best_val_acc = 0 # 保存最好的准确率 EPOCH = 3 # 训练轮数 for epoch in range(EPOCH): # 训练轮数 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader): # 批次数据 out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device), token_type_ids=token_type_ids.to(device)) # 模型输出 loss = criterion(out, labels.to(device)) # 计算损失 optimizer.zero_grad() # 清空梯度 loss.backward() # 反向传播 optimizer.step() # 优化器更新参数 # 每隔5个批次输出训练结果 if i % 5 == 0: out = out.argmax(dim=1) # 获取预测结果 acc = (out == labels.to(device)).sum().item() / len(labels) # 计算准确率 print("EPOCH:{}--第{}批次--损失:{}--准确率:{}".format(epoch + 1, i + 1, loss.item(), acc))

运行结果:

cuda EPOCH:1--第1批次--损失:0.5008983016014099--准确率:0.78 EPOCH:1--第6批次--损失:0.5052810907363892--准确率:0.75 EPOCH:1--第11批次--损失:0.4850313067436218--准确率:0.755 EPOCH:1--第16批次--损失:0.4301462471485138--准确率:0.83 EPOCH:1--第21批次--损失:0.39388778805732727--准确率:0.85 EPOCH:1--第26批次--损失:0.3695535361766815--准确率:0.855 EPOCH:1--第31批次--损失:0.35825812816619873--准确率:0.855 EPOCH:1--第36批次--损失:0.3288692533969879--准确率:0.875 EPOCH:2--第1批次--损失:0.31738394498825073--准确率:0.885 EPOCH:2--第6批次--损失:0.3121739625930786--准确率:0.87 EPOCH:2--第11批次--损失:0.30510687828063965--准确率:0.895 EPOCH:2--第16批次--损失:0.305753618478775--准确率:0.865 EPOCH:2--第21批次--损失:0.24456100165843964--准确率:0.92 EPOCH:2--第26批次--损失:0.2233615517616272--准确率:0.93 EPOCH:2--第31批次--损失:0.2816208302974701--准确率:0.89 EPOCH:2--第36批次--损失:0.24931633472442627--准确率:0.915 EPOCH:3--第1批次--损失:0.3053100109100342--准确率:0.885 EPOCH:3--第6批次--损失:0.2515011727809906--准确率:0.9 EPOCH:3--第11批次--损失:0.24241474270820618--准确率:0.915 EPOCH:3--第16批次--损失:0.2211739420890808--准确率:0.925 EPOCH:3--第21批次--损失:0.24276195466518402--准确率:0.91 EPOCH:3--第26批次--损失:0.27010777592658997--准确率:0.895 EPOCH:3--第31批次--损失:0.24304606020450592--准确率:0.9 EPOCH:3--第36批次--损失:0.26476508378982544--准确率:0.905
5,评估模型

模型训练好之后,我们要对模型进行性能评估,以及保存最优模型参数。

示例代码:

import torch from datasets import load_dataset from torch.utils.data import Dataset, DataLoader from transformers import BertTokenizer, BertModel # 自定义数据集 class MyDataset(Dataset): def __init__(self, split): # 加载训练集 train_dataset = load_dataset(path="csv", data_files="./weibo_senti_8k_train.csv") # 加载测试集 test_dataset = load_dataset(path="csv", data_files="./weibo_senti_2k_test.csv") if split == 'train': self.data = train_dataset['train'] elif split == 'test': self.data = test_dataset['train'] # 获取数据集大小 def __len__(self): return len(self.data) # 获取数据集的某个元素 def __getitem__(self, index): sentence = self.data[index]['sentence'] label = self.data[index]['label'] return sentence, label # 加载分词器 tokenizer = BertTokenizer.from_pretrained('./Bert-base-chinese') # 使用设备(GPU/CPU) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(device) # 加载预训练模型 pretrained_model = BertModel.from_pretrained('./Bert-base-chinese').to(device) # 定义下游任务(增量模型) class DownStreamModel(torch.nn.Module): def __init__(self): super(DownStreamModel, self).__init__() # 下游加一个全连接层,实现二分类任务 self.fc = torch.nn.Linear(768, 2) def forward(self, input_ids, attention_mask, token_type_ids): with torch.no_grad(): # 冻结Bert模型参数,不需计算梯度 # 获取最后一层隐藏层输出 output = pretrained_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) # 增量模型参与训练 out = self.fc(output.last_hidden_state[:, 0, :]) return out # 对传入数据进行编码 def collate_fn(data): sents = [i[0] for i in data] labels = [i[1] for i in data] # 编码 # 批量编码句子 out = tokenizer.batch_encode_plus( batch_text_or_text_pairs=sents, # 输入的文本 add_special_tokens=True, # 添加特殊标记 max_length=256, # 最大长度 padding='max_length', # 填充 truncation=True, # 截断 return_tensors='pt', # 返回pytorch张量 return_token_type_ids=True, # 返回token_type_ids 区分不同句子或段落的类型标识 return_attention_mask=True, # 返回attention_mask 标记有效token位置的掩码 return_special_tokens_mask=True # 返回special_tokens_mask 标识特殊token(如[CLS]、[SEP])的位置掩码 ) return out['input_ids'], out['attention_mask'], out['token_type_ids'], torch.tensor(labels) # 创建数据集 train_dataset = MyDataset('train') # 训练集 train_loader = DataLoader( dataset=train_dataset, # 数据集 batch_size=200, # 批次大小 shuffle=True, # 是否打乱数据 drop_last=True, # 丢弃最后一个批次数据 collate_fn=collate_fn # 对加载的数据进行编码 ) test_dataset = MyDataset('test') # 测试集 test_loader = DataLoader( dataset=test_dataset, # 数据集 batch_size=200, # 批次大小 shuffle=True, # 是否打乱数据 drop_last=True, # 丢弃最后一个批次数据 collate_fn=collate_fn # 对加载的数据进行编码 ) if __name__ == '__main__': model = DownStreamModel().to(device) # 创建模型 optimizer = torch.optim.AdamW(model.parameters()) # 优化器 criterion = torch.nn.CrossEntropyLoss() # 定义损失函数 best_val_acc = 0 # 保存最好的准确率 EPOCH = 3 # 训练轮数 for epoch in range(EPOCH): # 训练轮数 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader): # 批次数据 out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device), token_type_ids=token_type_ids.to(device)) # 模型输出 loss = criterion(out, labels.to(device)) # 计算损失 optimizer.zero_grad() # 清空梯度 loss.backward() # 反向传播 optimizer.step() # 优化器更新参数 # 每隔5个批次输出训练结果 if i % 5 == 0: out = out.argmax(dim=1) # 获取预测结果 acc = (out == labels.to(device)).sum().item() / len(labels) # 计算准确率 print("EPOCH:{}--第{}批次--损失:{}--准确率:{}".format(epoch + 1, i + 1, loss.item(), acc)) # 验证模型 model.eval() # 评估模式 Dropout关闭 with torch.no_grad(): # 评估模式,不需要计算梯度 for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(test_loader): out = model(input_ids=input_ids.to(device), attention_mask=attention_mask.to(device), token_type_ids=token_type_ids.to(device)) out = out.argmax(dim=1) acc = (out == labels.to(device)).sum().item() / len(labels) if acc > best_val_acc: best_val_acc = acc torch.save(model.state_dict(), "best_model.pth") print(f"测试集准确率EPOCH:{epoch}-第{i}批次:模型保存,准确率:{acc}") # 保存最后一轮模型 torch.save(model.state_dict(), "last_model.pth") print(f"最后一轮模型保存,:准确率:{acc}")

运行结果:

cuda EPOCH:1--第1批次--损失:0.6369971632957458--准确率:0.695 EPOCH:1--第6批次--损失:0.6052521467208862--准确率:0.685 EPOCH:1--第11批次--损失:0.5286625623703003--准确率:0.77 EPOCH:1--第16批次--损失:0.4174182415008545--准确率:0.83 EPOCH:1--第21批次--损失:0.41344115138053894--准确率:0.81 EPOCH:1--第26批次--损失:0.3910037875175476--准确率:0.87 EPOCH:1--第31批次--损失:0.3680213987827301--准确率:0.85 EPOCH:1--第36批次--损失:0.3817676901817322--准确率:0.855 EPOCH:2--第1批次--损失:0.3963841199874878--准确率:0.84 EPOCH:2--第6批次--损失:0.3516421914100647--准确率:0.85 EPOCH:2--第11批次--损失:0.330654114484787--准确率:0.845 EPOCH:2--第16批次--损失:0.3345922529697418--准确率:0.865 EPOCH:2--第21批次--损失:0.3087370693683624--准确率:0.885 EPOCH:2--第26批次--损失:0.25769323110580444--准确率:0.92 EPOCH:2--第31批次--损失:0.2792946696281433--准确率:0.885 EPOCH:2--第36批次--损失:0.29899129271507263--准确率:0.905 EPOCH:3--第1批次--损失:0.3139827847480774--准确率:0.855 EPOCH:3--第6批次--损失:0.27809959650039673--准确率:0.895 EPOCH:3--第11批次--损失:0.2725857198238373--准确率:0.885 EPOCH:3--第16批次--损失:0.30161210894584656--准确率:0.885 EPOCH:3--第21批次--损失:0.26055067777633667--准确率:0.925 EPOCH:3--第26批次--损失:0.22951321303844452--准确率:0.895 EPOCH:3--第31批次--损失:0.2995443642139435--准确率:0.87 EPOCH:3--第36批次--损失:0.3246515691280365--准确率:0.87 测试集准确率EPOCH:2-第0批次:模型保存,准确率:0.975 最后一轮模型保存,:准确率:0.955

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1191692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Glyph语音转写可视化:声谱图推理部署实战

Glyph语音转写可视化:声谱图推理部署实战 1. Glyph是什么?用图像处理长文本的新思路 你有没有遇到过这样的问题:一段长达几万字的会议录音转写稿,光是加载就卡得不行,更别提让大模型去分析总结了?传统语言…

Z-Image-Turbo权限管理:限制访问保障模型安全使用

Z-Image-Turbo权限管理:限制访问保障模型安全使用 Z-Image-Turbo 是一款功能强大的图像生成模型,其配套的 UI 界面让使用者能够通过可视化操作快速完成图像生成任务。整个界面设计简洁直观,主要包含提示词输入区、参数调节滑块、生成按钮以及…

电子信息毕设 stm32智能鱼缸监控投喂系统(源码+硬件+论文)

文章目录 0 前言1 主要功能2 硬件设计(原理图)3 核心软件设计4 实现效果5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉…

第十五章 KUKA机器人与S7-1200进行Profinet通讯

1、概述 KUKA机器人与S7-1200进行Profinet通讯实战案例解析。 2、硬件配置 2.1硬件配置 名称 型号 数量 PLC

godot引擎基础学习笔记9(C#)

一、游戏界面在创建界面场景的时候,会有一个专门用于用户界面的场景,点击创建会生成一个control节点相关常用属性:ClipContents:能够进行子节点的裁剪,启用会将当前节点外的子节点内容隐藏掉custom_minimum_size&#…

专为细菌吞噬研究设计的智能荧光工具

了解细胞如何吞噬细菌?现在可以看得更清楚!Protonex™ Red 670-大肠杆菌结合物是一种即用型试剂,让您实时观察活细胞内的细菌吞噬和酸化过程。产品亮点:新型pH敏感荧光团在中性环境中"静默",进入吞噬体酸性环…

GPEN输出文件命名规则?自定义保存路径实战说明

GPEN输出文件命名规则?自定义保存路径实战说明 GPEN人像修复增强模型镜像 本镜像基于 GPEN人像修复增强模型 构建,预装了完整的深度学习开发环境,集成了推理及评估所需的所有依赖,开箱即用。 1. 镜像环境说明 组件版本核心框架…

OCR未来方向:cv_resnet18_ocr-detection支持动态输入尺寸

OCR未来方向:cv_resnet18_ocr-detection支持动态输入尺寸 1. 引言:为什么动态输入尺寸是OCR的关键突破 你有没有遇到过这种情况?一张高分辨率的工程图纸,文字密密麻麻,但用常规OCR模型一检测,小字全丢了&…

Java Web 人口老龄化社区服务与管理平台系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】

💡实话实说:CSDN上做毕设辅导的都是专业技术服务,大家都要生活,这个很正常。我和其他人不同的是,我有自己的项目库存,不需要找别人拿货再加价。我就是个在校研究生,兼职赚点饭钱贴补生活费&…

Java SpringBoot+Vue3+MyBatis 宠物领养系统系统源码|前后端分离+MySQL数据库

摘要 随着社会经济的快速发展和人们生活水平的提高,宠物已成为许多家庭的重要成员,宠物领养需求也随之增长。然而,传统的宠物领养方式存在信息不对称、流程繁琐等问题,导致许多流浪动物难以找到合适的家庭。为了解决这一问题&…

HeartMuLa - 用AI创作歌曲 输入歌词即可创作音乐 支持50系显卡 一键整合包下载

HeartMuLa 是一个开源的“音乐大模型工具库”,它能把歌词和标签转化为完整的音乐作品,还能做歌词转录、音乐编码和跨模态检索。它的特点是多语言支持(中、英、日、韩、西班牙语等)、高保真音频生成,以及和文字描述的紧…

基于SpringBoot+Vue的华强北商城二手手机管理系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】

摘要 随着互联网技术的快速发展,电子商务已成为现代商业活动的重要组成部分。二手手机市场因其高性价比和环保特性,吸引了大量消费者,但传统的线下交易模式存在信息不对称、交易效率低等问题。华强北作为中国最大的电子产品集散地&#xff0c…

性价比高的半自动穿管机企业

性价比高的半自动穿管机企业如何选择?行业深度解析与实用指南在电线电缆、汽车线束、光纤光缆等制造业中,半自动穿管机作为提升生产效率、降低人工成本的关键设备,其市场需求持续增长。对于众多采购企业而言,如何在众多供应商中找…

三轴MEMS加速度计感知万物运动,精准赋能无限可能

在智能化的浪潮中,精确感知物理世界的运动与姿态,是无数设备从“机械执行”迈向“自主决策”的第一步。三轴MEMS加速度计,正是实现这一跨越的核心感官。而ER-3MA-09,以其卓越的低噪声、低温漂和长期稳定性,正成为高精度…

动态住宅IP是什么呢?都有什么用途?

随着互联网技术的发展,代理 IP 已成为跨境电商、数据采集、海外社媒运营等业务的核心基础设施,而动态住宅 IP 代理凭借其贴合真实用户的特性,在各类跨境场景中展现出独特的应用价值。不同业务对代理 IP 的需求差异显著,本文将为大…

基于SpringBoot+Vue的夕阳红公寓管理系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】

摘要 随着人口老龄化趋势加剧,老年人的居住需求日益受到社会关注。传统的养老院管理模式存在信息化程度低、管理效率不足等问题,无法满足现代养老服务的精细化需求。夕阳红公寓管理系统旨在通过信息化手段提升老年公寓的管理效率和服务质量,为…

4款免费低代码工具推荐:中小企业数字化转型的零成本解决方案

在企业数字化转型浪潮中,低代码工具凭借“可视化搭建、降本提效”的核心优势,成为中小企业及创业团队的刚需工具。尤其是免费版低代码软件,既能满足基础业务需求,又能规避前期投入风险。本文给大家找到几款口碑出众、实用性强的免…

从初级测试工程师进阶中级测试岗,一篇避开弯路...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 普通程序员 35 岁…

GPEN判别器训练技巧?学习率与epoch数调参经验分享

GPEN判别器训练技巧?学习率与epoch数调参经验分享 GPEN(GAN-Prior based Enhancement Network)作为当前人像修复领域表现突出的模型之一,凭借其在细节恢复、肤色自然度和五官一致性上的优异表现,被广泛应用于老照片修…