2.3 预训练模型
预训练模型是通过在大规模未标记数据上进行学习而生成的模型,它们能够捕捉数据中的统计特性和语义信息。这些模型通常在通用任务上进行预训练,如语言模型的掩码语言建模或图像模型的自监督学习,然后在特定任务上进行微调,以提高性能和泛化能力。例如本章前面介绍的多模态大模型VILBERT、VisualBERT、OpenAI CLIP、UNITER和LXMERT都可以进行预训练,它们都是基于大规模未标记数据进行预训练的多模态模型,这些模型在预训练之后,通常可以在各种视觉和语言任务上进行微调,以适应特定的应用场景,提高性能。
2.3.1 预训练模型简介
预训练模型是指在大规模未标记数据上进行预训练的深度学习模型。这些模型通常通过无监督或半监督学习的方式,在大规模数据上学习数据的表示和语义信息,而无需人工标注的标签。预训练模型的目的是捕捉数据中的统计结构和语义信息,以便在特定任务上进行微调,提高性能和泛化能力。
在实际应用中,预训练模型通常包括如下两个主要组成部分:
(1)模型架构:预训练模型的架构通常基于深度神经网络,如Transformer、CNN等。这些模型可以是单模态的(如仅处理文本或仅处理图像),也可以是多模态的(同时处理多种类型的数据,如图像和文本)。
(2)预训练任务:预训练模型在训练过程中执行的任务,通常是无监督或半监督的。常见的预训练任务包括:
- 语言模型预训练:如掩码语言建模(Masked Language Modeling,MLM),模型尝试从部分文本中预测被遮盖的单词。
- 图像自监督学习:模型尝试从图像中学习有用的表示,如图像补全、颜色化等任务。
- 多模态预训练:模型同时处理多模态数据,如图像和文本,学习它们之间的关联性。
预训练模型在预训练任务上学习到的参数和表示可以被迁移或微调到特定的下游任务上,如文本分类、图像分类、目标检测、文本生成等,以提高模型在这些任务上的性能。
常见的预训练模型包括BERT、GPT、ResNet、Vision Transformers(ViT)等,这些预训练模型已经成为许多自然语言处理和计算机视觉任务的重要基础。
2.3.2 使用预训练模型
使用预训练模型是利用已经在大规模数据上进行预训练的模型参数,在特定任务上进行微调或者特征提取的一种常见策略。通过使用预训练模型,可以帮助提高模型性能、加速模型训练,并且降低了在大规模数据上训练深度神经网络的成本,因此在实践中被广泛采用。使用预训练模型的一般流程如下所示:
(1)选择预训练模型
- 根据任务需求和数据集特点选择合适的预训练模型,例如BERT、GPT、ResNet等。
- 可以根据任务是文本分类、目标检测、语言生成等来选择相应的预训练模型。
(2)获取预训练模型权重:从预训练模型的官方发布源、Hugging Face模型中心或其他可靠来源获取模型的权重文件。
(3)加载预训练模型
- 使用深度学习框架(如PyTorch、TensorFlow等)加载预训练模型及其权重。
- 通常可以使用相应框架提供的库来加载,如Hugging Face Transformers库。
(4)准备数据
- 根据任务准备相应的数据集,包括训练集、验证集和测试集。
- 对数据进行预处理,如标记化、归一化、分批等操作。
(5)微调模型
- 如果任务需要,可以在特定任务的数据集上对预训练模型进行微调。
- 设置损失函数、优化器等参数,并在训练集上进行训练。
(6)评估模型性能
- 使用验证集评估微调后模型的性能,调整超参数以提高性能。
- 可以使用准确率、精确率、召回率等指标进行评估。
(7)模型应用
- 在测试集上评估最终模型的性能,获取模型在真实数据上的表现。
- 根据需求将模型部署到生产环境中进行应用。
请看下面的实例,演示了使用预训练的 UNITER 模型(基于 BERT)处理图像和文本的多模态任务的过程。首先加载了预训练的 UNITER 模型和分词器,然后对给定的图像和文本进行预处理。接着,将图像和文本输入到模型中,获取模型的表示,然后通过自定义的头部网络进行分类预测,最后输出预测结果的概率。
实例2-1:使用预训练的 UNITER 模型处理任务(源码路径:codes/2/xia.py和Unmo.py)
(1)实例文件xia.py用于从指定 URL 下载预训练 UNITER 模型的权重文件(包括 pytorch_model.bin、config.json 和 vocab.txt),并将它们保存到指定目录。如果文件已经存在于目标目录中,则不会重复下载。文件xia.py的具体实现代码如下所示。
import os
import requests
# 下载路径和保存路径
base_url = 'https://github.com/visualjoyce/transformers4vl-uniter-base/raw/main/'
output_dir = r'C:\qinghua\多模态\codes\2'
model_name = 'uniter-base'
# 创建目录(如果不存在)
os.makedirs(output_dir, exist_ok=True)
# 下载模型权重文件
files_to_download = ['pytorch_model.bin', 'config.json', 'vocab.txt']
for file_name in files_to_download:
url = base_url + file_name
output_path = os.path.join(output_dir, f'{model_name}_{file_name}')
# 下载文件
if not os.path.exists(output_path):
response = requests.get(url, stream=True)
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f'{file_name} downloaded to {output_path}')
else:
print(f'{file_name} already exists at {output_path}')
(2)编写文件Unmo.py,功能是使用预训练的UNITER模型处理图像和文本,并定制网络头部进行预测。具体实现代码如下所示。
from tensorflow_datasets.image_classification.colorectal_histology_test import num_classes
from transformers import BertModel, BertTokenizer
from PIL import Image
from torchvision import transforms
import torch.nn as nn
import torch
# 加载模型和tokenizer
model_name = "visualjoyce/transformers4vl-uniter-base"
model = BertModel.from_pretrained(model_name)
tokenizer = BertTokenizer.from_pretrained(model_name)
# 定义自定义头部
class CustomHead(nn.Module):
def __init__(self, input_size, output_size):
super(CustomHead, self).__init__()
self.fc = nn.Linear(input_size, output_size)
def forward(self, x):
x = self.fc(x)
return x
# 图像预处理
def preprocess_image(image_path):
image = Image.open(image_path).convert("RGB")
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
image = transform(image).unsqueeze(0) # 增加batch维度
return image
# 文本预处理
def preprocess_text(question):
inputs = tokenizer(question, return_tensors="pt", padding=True, truncation=True)
return inputs
# 加载图像和文本
image_path = "path_to_image.png" # 修改为图像的实际路径
question = "What is the color of the cat?"
# 预处理图像和文本
image_tensor = preprocess_image(image_path)
text_inputs = preprocess_text(question)
# 将图像和文本输入到模型中
outputs = model(**text_inputs)
# 获取模型的最后一层表示
last_hidden_states = outputs.last_hidden_state
# 定义自定义头部,并传入模型表示的大小和自定义头部输出的大小
custom_head = CustomHead(input_size=last_hidden_states.shape[-1], output_size=num_classes)
logits = custom_head(last_hidden_states)
# 示例:在logits上添加softmax层并进行预测
softmax = nn.Softmax(dim=1)
probs = softmax(logits)
# 输出预测结果
print(probs)
上述代码的实现流程如下所示:
- 加载预训练模型和分词器:使用 visualjoyce/transformers4vl-uniter-base 预训练模型的权重初始化 BERT 模型。使用相同的预训练模型初始化分词器。
- 定义自定义头部网络:创建了一个自定义头部网络 CustomHead,该网络接受模型表示作为输入,并输出预测结果。在初始化函数中定义了一个全连接层,输入大小为模型表示的大小,输出大小为数据集的类别数量。
- 图像预处理和文本预处理:通过preprocess_image 函数加载、调整大小和标准化图像。通过preprocess_text 函数将文本转换为模型可以接受的格式。
- 加载图像和文本数据:设置图像和文本的路径和问题,然后对图像和文本进行预处理。
- 将图像和文本输入到模型中:将预处理后的文本输入 BERT 模型中,获取模型输出。
- 获取模型的最后一层表示:从模型输出中获取最后一层的表示。
- 定义自定义头部并进行预测:使用自定义头部网络对模型表示进行分类预测,将预测结果通过 softmax 函数得到概率。
- 输出预测结果:打印预测结果概率,执行后会输出:
You are using a model of type uniter to instantiate a model of type bert. This is not supported for all configurations of models and can yield errors.
Some weights of BertModel were not initialized from the model checkpoint at visualjoyce/transformers4vl-uniter-base and are newly initialized: ['encoder.layer.6.output.LayerNorm.weight', 'encoder.layer.2.attention.self.value.bias', 'encoder.layer.11.output.dense.weight',
###省略部分输出
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Asking to truncate to max_length but no maximum length is provided and the model has no predefined maximum length. Default to no truncation.
tensor([[[0.1159, 0.0673, 0.1049, 0.0776, 0.1197, 0.0731, 0.0883, 0.0689],
[0.0823, 0.1458, 0.0731, 0.0669, 0.0518, 0.1224, 0.1127, 0.1070],
[0.1064, 0.0826, 0.0909, 0.0919, 0.1011, 0.0750, 0.0639, 0.1121],
[0.0924, 0.0629, 0.1253, 0.0675, 0.0825, 0.1019, 0.0811, 0.0725],
[0.1722, 0.0981, 0.1051, 0.1325, 0.0635, 0.1115, 0.1225, 0.1473],
[0.0919, 0.1118, 0.1306, 0.2269, 0.0681, 0.1108, 0.1476, 0.0910],
[0.0796, 0.0823, 0.1016, 0.0819, 0.1281, 0.0842, 0.0816, 0.0920],
[0.1339, 0.1364, 0.0656, 0.0850, 0.1175, 0.1316, 0.1187, 0.1114],
[0.0567, 0.0842, 0.0508, 0.0561, 0.1148, 0.0971, 0.0791, 0.0889],
[0.0687, 0.1285, 0.1521, 0.1138, 0.1529, 0.0924, 0.1046, 0.1090]]],
grad_fn=<SoftmaxBackward0>)
在上面的输出结果中,请先暂时忽略前面的警告,只看后面的输出结果。这个输出结果是经过 softmax 处理后的预测概率,这是一个三维的张量,具体含义如下:
- 第一维:表示 batch 中每个样本的索引。
- 第二维:表示每个样本的预测结果,这里每个样本有 8 个类别的预测概率。
- 第三维:表示每个类别的预测概率。
在这个例子中,输出的张量是一个 [1, 10, 8] 的张量,具体含义如下所示:
- 1 表示只有一个样本。
- 10 表示这个样本有 10 个预测结果。
- 8 表示每个预测结果有 8 个类别的预测概率。
所以,例如第一个样本的第一个预测结果,预测各个类别的概率分别为 [0.1395, 0.1044, 0.0686, 0.0904, 0.1178, 0.1368, 0.0868, 0.0868]。
上面的输出结果表明模型对每个类别的预测概率,开发者可以根据具体的需求选择最高概率的类别作为模型的预测结果。
2.3.3 预训练模型的微调
在前面实例2-1的输出结果中输出如下所示的警告信息:
You are using a model of type uniter to instantiate a model of type bert. This is not supported for all configurations of models and can yield errors.
####省略后面的警告信息
这是因为在实例2-1中加载了一个 visualjoyce/transformers4vl-uniter-base 的模型,但是由于该模型是未经过微调的,因此输出的结果可能是随机的。要解决这个问题,可以对这个模型上进行微调,或者使用一个经过预训练并且在你的数据上微调过的模型。
预训练模型的微调是指将预先训练好的模型在特定任务上进行进一步训练,以适应特定任务的数据和要求。
1. 预训练模型微调的原理
- 迁移学习:微调利用了迁移学习的思想,预训练模型在大规模数据上进行了预训练,学习到了通用的语言或图像表示。微调则在特定任务的数据集上进一步调整这些表示,使其适应特定任务。
- 参数调整:在微调过程中,模型的参数会根据特定任务的数据进行调整。通常是在预训练模型的基础上,通过反向传播和优化算法(如随机梯度下降)来微调参数。
2. 预训练模型微调的架构
(1)预训练模型:常见的预训练模型包括 BERT、GPT、RoBERTa、UniLM、Vision Transformers(ViT)等,它们在大规模文本或图像数据上进行了预训练。
(2)微调层:在预训练模型之上添加了用于特定任务的额外层,通常包括自定义的分类头部、池化层等。这些层用于从预训练模型的输出中提取特征,并进行特定任务的预测。
(3)损失函数:根据任务的性质选择合适的损失函数,如交叉熵损失函数用于分类任务,均方误差用于回归任务等。
(4)Fine-tuning 过程
- 前向传播:输入数据经过预训练模型和微调层,得到预测结果。
- 计算损失:预测结果与真实标签计算损失。
- 反向传播:通过反向传播计算梯度,并更新模型参数以最小化损失。
- 优化:使用优化算法(如随机梯度下降、Adam 等)来更新模型的参数。
- 重复迭代:在训练数据上多次迭代,直到模型收敛或达到设定的停止条件。
3. 微调步骤
(1)加载预训练模型:首先,加载预训练模型及其权重,可以使用像 Hugging Face Transformers 这样的库从预训练模型的 URL 或本地文件加载模型。
(2)修改模型结构:根据任务的特点,可能需要修改模型的结构。例如,添加自定义的分类头部或其他层来适应特定的任务。
(3)准备数据:准备用于微调的数据集。数据集通常需要进行预处理,并转换为模型所需的格式。
(4)定义训练参数:设置微调的超参数,如学习率、批量大小、训练轮数等。
(5)微调模型:使用准备好的数据集对模型进行训练。在训练过程中,模型的权重会根据数据集进行调整,以更好地适应特定任务。
(6)评估模型:使用验证集或测试集评估微调后的模型性能。这可以帮助确定模型是否已经适当地微调,并且是否需要进一步的调整。
(7)调整和优化:根据评估结果,可能需要调整模型结构或超参数,并重新进行微调,直到达到满意的性能。
(8)应用和部署:在完成微调后,可以将模型应用于实际任务中,并进行部署供使用。
总之,微调可以使预训练模型适应各种任务,例如文本分类、命名实体识别、图像分类等,而无需从头开始训练模型。
请看下面的实例,功能是使用预训练的 BERT 模型对图像和文本进行处理,并在自定义的分类任务上进行微调。本实例的实现步骤包括加载预训练的 BERT 模型和 tokenizer,预处理图像和文本数据,定义自定义头部和微调模型,设置损失函数和优化器,进行模型微调,然后在微调后进行预测。代码中展示了三个训练周期(epoch)的损失变化,并最终输出预测的类别。
实例2-2:对UNITER 模型进行微调(源码路径:codes/2/tiao.py)
文件tiao.py的具体实现代码如下所示。
import torch
from transformers import BertModel, BertTokenizer, BertConfig
from PIL import Image
from torchvision import transforms
import torch.nn as nn
import torch.optim as optim
# 加载预训练的 BERT 模型和 tokenizer
model_name = "visualjoyce/transformers4vl-uniter-base" # 或者你使用的其他预训练 BERT 模型
config = BertConfig.from_pretrained(model_name)
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name, config=config)
# 自定义头部
class CustomHead(nn.Module):
def __init__(self, input_size, output_size):
super(CustomHead, self).__init__()
self.fc = nn.Linear(input_size, output_size)
def forward(self, x):
x = self.fc(x)
return x
# 图像预处理
def preprocess_image(image_path):
image = Image.open(image_path).convert("RGB")
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
image = transform(image).unsqueeze(0) # 添加 batch 维度
return image
# 文本预处理
def preprocess_text(question):
inputs = tokenizer(question, return_tensors="pt", padding=True, truncation=True)
return inputs
# 加载图像和文本
image_path = "path_to_image.png" # 修改为图像的实际路径
question = "What is the color of the cat?"
# 预处理图像和文本
image_tensor = preprocess_image(image_path)
text_inputs = preprocess_text(question)
# 准备微调任务的标签(示例)
labels = torch.tensor([1]) # 示例标签
# 自定义微调部分
class MyModel(nn.Module):
def __init__(self, bert_model, num_classes):
super(MyModel, self).__init__()
self.bert = bert_model
self.custom_head = CustomHead(input_size=self.bert.config.hidden_size, output_size=num_classes)
def forward(self, input_ids, attention_mask, image):
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
pooled_output = outputs.pooler_output
logits = self.custom_head(pooled_output)
return logits
# 初始化模型
num_classes = 2 # 假设输出类别数为 2
model = MyModel(model, num_classes)
# 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=1e-5)
# 模型微调
model.train()
num_epochs = 3
for epoch in range(num_epochs):
optimizer.zero_grad()
logits = model(input_ids=text_inputs.input_ids, attention_mask=text_inputs.attention_mask, image=image_tensor)
loss = loss_fn(logits, labels)
loss.backward()
optimizer.step()
print(f"Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item()}")
# 在微调完成后进行预测
model.eval()
with torch.no_grad():
logits = model(input_ids=text_inputs.input_ids, attention_mask=text_inputs.attention_mask, image=image_tensor)
predicted_class = torch.argmax(logits, dim=1)
print("Predicted class:", predicted_class.item())
上述代码的实现流程如下:
- 首先,加载预训练的 BERT 模型和 tokenizer,定义自定义的头部和预处理函数。加载配置文件和预训练模型,定义自定义头部用于最终的分类任务,定义图像和文本的预处理函数以适应模型的输入需求。
- 然后,加载图像和文本数据,并进行预处理。图像通过 PIL 库打开并转换为 RGB 格式,然后通过 torchvision 库进行标准化和转换,文本通过 tokenizer 进行分词和编码。
- 接着,准备微调任务的标签,并定义包含 BERT 模型和自定义头部的模型结构。标签是用于监督学习的目标变量,模型结构包括预训练的 BERT 模型和自定义的分类头部。
- 然后,定义损失函数和优化器,进行模型微调。使用交叉熵损失函数和 AdamW 优化器,针对预处理后的图像和文本输入,迭代优化模型参数,以最小化训练过程中的损失函数值。
- 最后,在微调完成后进行预测,并输出预测结果。切换模型到评估模式,使用微调后的模型对新的输入数据进行预测,输出预测的类别标签。这样就完成了对预训练 UNITER 模型的微调和应用。执行后会输出:
Epoch [1/3], Loss: 0.6956343054771423
Epoch [2/3], Loss: 0.28804779052734375
Epoch [3/3], Loss: 0.07029898464679718
Predicted class: 1
标签:模态,框架,训练,模型,微调,model,文本,image
From: https://blog.csdn.net/asd343442/article/details/141106817