【NLP】多标签分类【下】

文章目录

  • 简介
  • 个人博客与相关链接
  • 1 实验数据与任务说明
  • 2 模型介绍
    • 2.1 Transformer
      • Transformer能做什么?
    • 2.2 Hugging Face
      • Hugging Face的Transformers库
      • 社区支持和资源
      • 预训练模型的应用
    • 2.3 T5模型(Text-To-Text Transfer Transformer)
      • T5的核心理念
      • T5的架构和训练方法
      • T5在多标签分类任务中的运用
  • 3 实验步骤
    • 3.1 数据预处理
    • 3.2 模型训练与测试
      • 模型选择
      • 参数设置
    • 3.3 结果转化与评估
  • 4 代码与实验
    • 4.1 数据预处理
    • 4.2 模型训练与预测
      • 自定义批处理函数
      • 自定义Dataset
      • 自定义LightningModule
      • 训练与预测函数
      • 保存结果以及任务启动
    • 4.3 结果转化与模型评估
  • 5 实验结果

简介

在《【NLP】多标签分类》系列的上一篇文章中,我们深入探讨了三种机器学习方法:Binary Relevance (BR)、Classifier Chains (CC) 以及 Label Powerset (LP),旨在解决多标签分类的挑战。这些方法各展所长,为我们提供了不同角度解析和处理多标签问题的视角。继先前对这些机器学习方法的详尽分析之后,本篇文章转向更为先进的解决策略——专注于序列生成方法,并以Transformer模型的一种变体,即T5预训练模型为核心,进行实验探索。

本文将不仅详细介绍如何利用T5模型对多标签分类任务进行微调,而且还将通过实验对比,展现其相较于之前讨论的传统方法在性能上的优势和潜在应用价值。通过精心设计的实验和深入的结果分析,揭示序列生成方法特别是Transformer架构的强大能力和灵活性。

个人博客与相关链接

本文相关代码和数据集已同步上传github: issey_Kaggle/MultiLabelClassification at main · iceissey/issey_Kaggle (github.com)

本文代码(Notebook)已公布至kaggle: Transformer-Multi-Label-Classification (kaggle.com)

博主个人博客链接:issey的博客 - 愿无岁月可回首

1 实验数据与任务说明

数据来源:Multi-Label Classification Dataset (kaggle.com)

任务说明:

  • 背景:NLP——多标签分类数据集。
  • 内容:该数据集包含6个不同的标签(计算机科学、物理学、数学、统计学、定量生物学、定量金融),用于根据摘要和标题对研究论文进行分类。 标签列中的值1表示该标签属于该论文,每篇论文可以有多个标签为1。

2 模型介绍

2.1 Transformer

Transformer模型自从2017年由Vaswani等人在论文《Attention Is All You Need》中首次提出以来,已经证明了其在多种自然语言处理任务上的强大能力。尽管本文不会深入讲解Transformer的详细架构及其组成模块,我们仍然强烈推荐感兴趣的读者参考原始论文以获得全面的理解。

Transformer能做什么?

Transformer的创新之处在于其独特的自注意力机制,使其能够在处理文本时更有效地捕捉长距离依赖关系。这一特性不仅提高了处理速度,还提升了模型对文本的理解深度,打开了自然语言处理领域的新篇章。以下是Transformer在NLP领域的一些关键应用:

  • 文本分类:Transformer能够理解复杂的文本结构和语义,使其在文本分类任务上表现优异,包括情感分析、主题识别等。
  • 机器翻译:由于其强大的语言模型能力,Transformer模型已成为机器翻译领域的主导技术,提供了更加流畅和准确的翻译结果。
  • 文本摘要:Transformer模型能够理解和提取文本的关键信息,生成准确且连贯的摘要,无论是抽取式还是生成式摘要。
  • 问答系统:利用其深度理解能力,Transformer能够从大量文本中提取答案,为问答系统提供强有力的支持。
  • 语言生成:Transformer的变体,如GPT系列,已经展示了在生成文本、编写代码等任务上的卓越能力,推动了创造性文本生成和自动编程的新发展。

2.2 Hugging Face

在深入探讨如何将Transformer模型应用于多标签分类任务之前,让我们先了解一下Hugging Face。作为一个致力于推进机器学习技术民主化的开源社区和公司,Hugging Face为研究者和开发者们提供了丰富的预训练模型库及相关工具,极大地简化了NLP任务的开发流程。

官网链接:Hugging Face – The AI community building the future.

Hugging Face的Transformers库

作为一个广泛使用的Python库,Hugging Face的Transformers库集合了数百种预训练的Transformer模型,支持轻松应用于文本分类、文本生成、问答等多种NLP任务。该库的一个主要优势是其提供了统一的接口,让不同的Transformer模型,比如BERT、GPT-2、RoBERTa等,在几乎不需修改代码的情况下就能互相替换使用。

社区支持和资源

Hugging Face不仅提供预训练模型,还维护着一个充满活力的社区,社区成员在此分享经验、解决方案及最佳实践。这样的平台为初学者和专家提供了交流与学习的机会,进一步推动了NLP领域的发展。更进一步,Hugging Face也提供了模型共享平台,允许研究者和开发者上传及分享自己训练的模型,进一步增强了社区资源。

预训练模型的应用

对于多标签分类任务而言,Hugging Face的Transformers库开辟了一个既简单又强大的途径,以便利用最先进的模型。用户可根据自身任务需求选择合适的预训练模型,并通过微调(fine-tuning)的方式使其适应具体的多标签分类任务,从而大幅度降低了模型开发和训练的时间及资源消耗。在接下来的部分中,我们会详细展示这一过程的实现,包括模型的选择、数据准备、训练以及性能评估等关键步骤。

2.3 T5模型(Text-To-Text Transfer Transformer)

本节将介绍我们在本次实验中使用的预训练模型T5,全称为Text-To-Text Transfer Transformer。T5模型以其创新性著称,其设计理念是将所有自然语言处理(NLP)任务转化为一个统一的文本到文本的格式。这种独特的通用性使得T5成为解决多标签分类等复杂任务的理想选择。

T5的核心理念

T5模型的设计核心在于将各种NLP任务统一到一个简单的框架中:接受文本输入并产生文本输出。这意味着无论是进行文本分类、翻译,还是处理更为复杂的多标签分类和问答任务,T5模型都以相同的方法处理,极大地提升了模型的灵活性和适用范围。

T5的架构和训练方法

T5遵循了经典的Encoder-Decoder架构,但在训练策略上进行了创新。它首先在大量文本数据上进行预训练,掌握语言的广泛知识,然后在特定任务的数据集上进行微调(fine-tuning)。这种结合预训练和微调的方法使T5在许多NLP任务上取得了卓越的表现。

T5在多标签分类任务中的运用

在多标签分类任务中,T5模型将任务视为一个文本到文本的转换问题:它将文章内容作为输入,并输出一系列的标签作为分类结果。这种方法简化了任务的处理流程,并允许T5利用其预训练阶段学到的丰富语言知识,以提升任务的处理效率和分类准确性。

3 实验步骤

本实验的主要步骤包括:1)数据预处理。2)模型训练与测试。3)结果转化与评估。

3.1 数据预处理

正如前一节所述,T5模型以序列生成的形式处理任务,即接收文本输入并产生文本输出。因此,我们需要将原始数据转换成符合这一格式的形式,以便模型能够有效处理。以下是我们的原始数据格式示例:

文本内容计算机科学物理数学统计学定量生物学定量金融学
这是一个文本示例。100100

为了将这些数据转换为适合序列生成任务的格式,我们需要将标签(即标记为1的类别)转化为一串文本标签,如下所示:

示例:

文本内容标签
这是一个文本示例。计算机科学;统计学

注意:标签之间可以使用其他符号进行隔开,本例中使用的是分号(;)。我们的目标是将标记为1的标签拼接成一条文本数据,以便模型可以将这些标签作为生成任务的一部分来处理。

3.2 模型训练与测试

模型选择

模型名称:T5-Small

模型链接:google-t5/t5-small · Hugging Face

参数设置

batch_size = 16
epochs = 5
learning_rate = 2e-5

3.3 结果转化与评估

在使用T5模型完成多标签分类任务后,我们会得到模型生成的文本序列作为输出。这些输出序列以文本形式列出了预测的标签,例如:

预测标签
统计学;定量生物学;定量金融学

为了对模型的性能进行评估,并使用我们在上篇文章中介绍的多标签分类评估方法,必须先将这些文本格式的标签转换回原始数据的格式,即将每个标签对应到它们各自的分类列上,并用0或1表示其是否被预测为该类。转换后的格式如下所示:

计算机科学物理数学统计学定量生物学定量金融学
000111

在这个转换过程中,我们首先将每个预测的标签字符串分割为单独的标签(在本例中,我们使用分号";"作为分隔符)。然后,我们检查每个原始标签列,并将其与分割后的标签进行匹配,如果预测中包含某个标签,则在相应的列中标记为1;如果不包含,则标记为0。这样,我们就能得到一个与原始数据格式相匹配的矩阵,便于我们采用上篇文章中介绍的评估方法来量化模型的性能。

4 代码与实验

该部分强烈建议搭配Kaggle使用,见"相关链接"部分。(如果觉得有帮助,可以顺便在Kaggle点个赞谢谢)

4.1 数据预处理

将原始的多标签分类数据集转换为适用于T5模型的格式。具体来说,我们将文章的标题和摘要合并为一个单独的文本输入,并将标记为1的多个标签合并为一个分号分隔的标签字符串。最终,这一预处理步骤将生成一个清晰的文本到文本格式,为T5模型的训练做好准备。

DataPreprocessing.py

import pandas as pd

"""准备数据"""
input_csv = "../../../archive/train.csv"
data = pd.read_csv(input_csv)
print(len(data))
label_columns = data.columns[-6:]  # 提取labels列
print(label_columns)

data['text'] = data['TITLE'] + " " + data['ABSTRACT']  # 准备text
print(data['text'].head())

data['labels'] = data[label_columns].apply(lambda x: '; '.join(x.index[x == 1]), axis=1)
print(data['labels'])
# Displaying the updated dataset
preprocessed_data = data[['text', 'labels']]
print(preprocessed_data.head())

# 存储为新的 CSV 文件
output_path = "../../../archive/preprocessed_data.csv"
preprocessed_data.to_csv(output_path, index=False)

4.2 模型训练与预测

本次仍然使用了Pytorch以及Pytorch lightning作为实验框架,关于Pytorch lightning的使用方法请自行查阅官网。

Pytorch lightning: Welcome to ⚡ PyTorch Lightning — PyTorch Lightning 2.2.1 documentation

该部分对应文件名:Transformer.py

自定义批处理函数

def collate_fn(batch):
    """
    自定义批处理函数
    """
    texts = [item['text'] for item in batch]
    labels = [item['labels'] for item in batch]
    # 使用 tokenizer 对文本和标签进行编码,最大长度512
    encoding = tokenizer(texts, padding=True, truncation=True, max_length=512, return_tensors="pt")
    # 使用 tokenizer 的 target_tokenizer 对标签进行编码
    with tokenizer.as_target_tokenizer():
        labels_encoding = tokenizer(labels, padding=True, truncation=True, max_length=512, return_tensors="pt")
    # 将标签中的 pad token 替换为 -100,这是 T5 模型的要求
    labels_encoding["input_ids"][labels_encoding["input_ids"] == tokenizer.pad_token_id] = -100

    return {
        'input_ids': encoding['input_ids'],
        'attention_mask': encoding['attention_mask'],
        'labels': labels_encoding['input_ids']
    }

自定义Dataset

class T5Dataset(Dataset):
    """自定义数据集"""

    def __init__(self, dataset):
        self.dataset = dataset

    def __getitem__(self, idx):
        item = self.dataset[idx]
        return {
            'text': item['text'],
            'labels': item['labels']
        }

    def __len__(self):
        return len(self.dataset)

自定义LightningModule

class T5FineTuner(pl.LightningModule):
    """自定义LightningModule"""

    def __init__(self, train_dataset, val_dataset, test_dataset, learning_rate=2e-5):
        super(T5FineTuner, self).__init__()
        self.validation_loss = []
        self.model = T5ForConditionalGeneration.from_pretrained('t5-small')
        self.learning_rate = learning_rate  # 微调
        self.train_dataset = train_dataset
        self.val_dataset = val_dataset
        self.test_dataset = test_dataset
        self.prediction = []

    def forward(self, input_ids, attention_mask, labels=None):
        output = self.model(input_ids=input_ids,
                            attention_mask=attention_mask,
                            labels=labels)
        return output

    def configure_optimizers(self):
        return AdamW(self.model.parameters(), lr=self.learning_rate)

    def train_dataloader(self):
        train_loader = DataLoader(dataset=self.train_dataset, batch_size=batch_size, collate_fn=collate_fn,
                                  shuffle=True)
        return train_loader

    def val_dataloader(self):
        val_loader = DataLoader(dataset=self.val_dataset, batch_size=batch_size, collate_fn=collate_fn,
                                shuffle=False)
        return val_loader

    def test_dataloader(self):
        test_loader = DataLoader(dataset=self.test_dataset, batch_size=batch_size, collate_fn=collate_fn,
                                 shuffle=False)
        return test_loader

    def training_step(self, batch, batch_idx):
        input_ids = batch['input_ids']
        attention_mask = batch['attention_mask']
        labels = batch['labels']
        output = self(input_ids, attention_mask, labels)
        loss = output.loss
        self.log('train_loss', loss, prog_bar=True, logger=True, on_step=True, on_epoch=True)  # 将loss输出在控制台
        return loss

    def validation_step(self, batch, batch_idx):
        input_ids = batch['input_ids']
        attention_mask = batch['attention_mask']
        labels = batch['labels']
        output = self(input_ids, attention_mask, labels)
        loss = output.loss
        self.log('val_loss', loss, prog_bar=False, logger=True, on_step=True, on_epoch=True)
        return loss

    def test_step(self, batch, batch_idx):
        input_ids = batch['input_ids']
        attention_mask = batch['attention_mask']
        self.model.eval()
        # 生成输出序列
        generated_ids = self.model.generate(input_ids=input_ids, attention_mask=attention_mask)
        # 将生成的token ids转换为文本
        generated_texts = [tokenizer.decode(generated_id, skip_special_tokens=True, clean_up_tokenization_spaces=True)
                           for generated_id in generated_ids]
        # 返回解码后的文本
        # print(generated_texts)
        self.prediction.extend(generated_texts)

训练与预测函数

def test(model, fast_run):
    trainer = pl.Trainer(fast_dev_run=fast_run)
    trainer.test(model)
    test_result = model.prediction
    # print(type(test_result))
    for text in test_result[:10]:
        print(text)
    return test_result


def train(fast_run):
    # 增加回调最优模型
    checkpoint_callback = ModelCheckpoint(
        monitor='val_loss',  # 监控对象为'val_loss'
        dirpath='../../archive/log/T5FineTuner_checkpoints',  # 保存模型的路径
        filename='Models-{epoch:02d}-{val_loss:.2f}',  # 最优模型的名称
        save_top_k=1,  # 只保存最好的那个
        mode='min'  # 当监控对象指标最小时
    )
    # 设置日志保存的路径
    log_dir = "../../archive/log"
    logger = TensorBoardLogger(save_dir=log_dir, name="T5FineTuner_logs")
    # Trainer可以帮助调试,比如快速运行、只使用一小部分数据进行测试、完整性检查等,
    # 详情请见官方文档https://lightning.ai/docs/pytorch/latest/debug/debugging_basic.html
    # auto自适应gpu数量
    trainer = pl.Trainer(max_epochs=epochs, log_every_n_steps=10, accelerator='gpu', devices="auto", fast_dev_run=fast_run,
                         callbacks=[checkpoint_callback], logger=logger)
    model = T5FineTuner(train_dataset, valid_dataset, test_dataset, learning_rate)
    trainer.fit(model)
    return model

保存结果以及任务启动

当在Kaggle上进行操作时,请注意,直接使用load_dataset函数从CSV文件加载数据集可能会导致错误。为了避免这个问题,推荐先使用pandas库将CSV文件读入为DataFrame,之后再将其转换为适合模型训练的格式。具体的代码实现和操作可以参考Kaggle笔记本中的相关部分。

def save_to_csv(test_dataset, predictions, filename="../../archive/test_predictions.csv"):
    with open(filename, mode='w', newline='', encoding='utf-8') as file:
        writer = csv.writer(file)
        writer.writerow(['text', 'true_labels', 'pred_labels'])

        for item, pred_label in zip(test_dataset, predictions):
            text = item['text']
            true_labels = item['labels']
            writer.writerow([text, true_labels, pred_label])


if __name__ == '__main__':
    data = load_dataset('csv', data_files={'train': '../../archive/preprocessed_data.csv'})["train"]
    # 分割数据集为训练集和测试+验证集
    train_testvalid = data.train_test_split(test_size=0.3, seed=42)
    # 分割测试+验证集为测试集和验证集
    test_valid = train_testvalid['test'].train_test_split(test_size=0.5, seed=42)
    # 现在我们有了训练集、验证集和测试集
    train_dataset = train_testvalid['train']
    valid_dataset = test_valid['train']
    test_dataset = test_valid['test']
    # 打印各个数据集的大小
    print("Training set size:", len(train_dataset))
    print("Validation set size:", len(valid_dataset))
    print("Test set size:", len(test_dataset))
    # 准备Dataset
    train_dataset = T5Dataset(train_dataset)
    valid_dataset = T5Dataset(valid_dataset)
    test_dataset = T5Dataset(test_dataset)
    # print(train_dataset.__len__())
    # print(train_dataset[0])

    # 初始化分词器
    tokenizer = T5Tokenizer.from_pretrained('t5-small')
    # 装载dataLoader
    train_dataloader = DataLoader(dataset=train_dataset, batch_size=batch_size,
                                  collate_fn=collate_fn, shuffle=True, drop_last=True)
    # 查看装载情况
    for i, batch in enumerate(train_dataloader):
        print(f"Batch {i + 1}")
        print("Input IDs:", batch['input_ids'])
        print("Input IDs shape:", batch['input_ids'].shape)
        print("Attention Mask:", batch['attention_mask'])
        print("Attention Mask shape:", batch['attention_mask'].shape)
        print("Labels:", batch['labels'])
        print("\n")
        if i == 0:
            break

    fast_run = True
    model = train(fast_run)
    # model = T5FineTuner.load_from_checkpoint(
    #     "../../archive/log/T5FineTuner_checkpoints/model-epoch=09-val_loss=0.32.ckpt",
    #     train_dataset=train_dataset, val_dataset=valid_dataset,
    #     test_dataset=test_dataset)
    pre_texts = test(model, fast_run)
    save_to_csv(test_dataset, pre_texts)

4.3 结果转化与模型评估

Estimate.py

import pandas as pd
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score
import numpy as np


def convert_labels(label_str):
    return label_str.split(';') if label_str else []


def clean_label(label):
    return label.strip()


# 读取提供的 CSV 文件
file_path = "../../archive/test_predictions.csv"
data = pd.read_csv(file_path)
# print(data.head())

# 提取并转换真实标签和预测标签
true_labels = [convert_labels(label_str) for label_str in data['true_labels']]
pred_labels = [convert_labels(label_str) for label_str in data['pred_labels']]
# 使用清理后的标签重新创建真实标签和预测标签列表
true_labels_cleaned = [list(map(clean_label, label_list)) for label_list in true_labels]
pred_labels_cleaned = [list(map(clean_label, label_list)) for label_list in pred_labels]


# 使用 MultiLabelBinarizer 对标签进行独热编码
mlb = MultiLabelBinarizer()
mlb.fit(true_labels_cleaned + pred_labels_cleaned)
y_true = mlb.transform(true_labels_cleaned)
y_pred = mlb.transform(pred_labels_cleaned)

print("Transformer(T5) Accuracy =", accuracy_score(y_true, y_pred))
print("Transformer(T5) Precision (micro-average) =", precision_score(y_true, y_pred, average='micro'))
print("Transformer(T5) Recall (micro-average) =", recall_score(y_true, y_pred, average='micro'))
print("Transformer(T5) F1 Score (micro-average) =", f1_score(y_true, y_pred, average='micro'))

print("\nAnother way to calculate accuracy:")
# 计算每一列的准确率
column_accuracies = np.mean(y_true == y_pred, axis=0)
# 为每列准确率添加列名
column_accuracy_with_labels = list(zip(mlb.classes_, column_accuracies))
# 计算列准确率的均值
mean_column_accuracy = np.mean(column_accuracies)

for acc in column_accuracy_with_labels:
    print(acc)
# print(column_accuracy_with_labels)
print("Average accuracy = ", mean_column_accuracy)

5 实验结果

本节汇总并比较了上篇和下篇文章中各种实验的结果。我们采用了几种不同的算法来处理多标签分类问题,包括Binary Relevance(BR)与Random Forest组合、Classifier Chains(CC)与Random Forest组合、Label Powerset(LP)与Random Forest组合、LP与SVM组合,以及使用了Transformer(T5)模型的序列生成方法。

通过对比准确率(Accuracy)、微观精确度(Precision_micro)、微观召回率(Recall_micro)和微观F1分数(F1_micro)这四个关键性能指标,我们发现:

  • 使用基于Random Forest的BR、CC和LP方法可以得到相对较好的预测性能。
  • 当LP与SVM组合使用时,性能有所提高,特别是在召回率和F1分数方面。
  • 最为显著的是,Transformer(T5)模型尤其在准确率、召回率和F1分数上达到了最高值。

具体数值如下所示:

AlgorithmsAcc P r e m i c r o Pre_{micro} Premicro R e m i c r o Re_{micro} Remicro F 1 m i c r o F1_{micro} F1micro
BR(RandomForest)0.44770.80380.49780.6149
CC(RandomForest)0.47870.80120.52770.6363
LP(RandomForest)0.53490.71790.58890.6470
LP(SVM)0.59140.73680.72450.7306
Transformer(T5)0.64270.79940.78400.7916

从结果中我们可以得出结论,Transformer模型在处理复杂的多标签分类任务时,展现出了其强大的能力。这也表明了序列到序列模型,在NLP领域的广泛应用潜力和有效性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VUE3组合式API

create-vue create-vue是Vue官方新的脚手架工具,底层切换到了vite,为开发提供极速相应 使用create-vue 1.安装16.0或者更高版本的Node.js 2.npm init vuelatest指令会安装并执行create-vue 项目目录和关键文件 组合式API Vue 3引入了组合式API(Com…

走进MySQL:从认识到入门(针对初学者)

一,引言 MySQL是一款久负盛名且广泛应用的关系型数据库管理系统,自1995年Michael Widenius和David Axmark在瑞典和芬兰发起研发以来,其发展历程可谓辉煌且深远。作为开源软件的代表,MySQL以其卓越的成本效益、高性能及高可靠性赢得…

Linux使用宝塔面板部署Discuz结合内网穿透实现公网访问本地论坛

文章目录 前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 前言 Crossday Discuz! Board(以下简称 Discuz!)是一套通用的社区论坛软件系统,用户可以在不需要任何编程的基础上&a…

ZLMediaKit ubantu 下编译

1、获取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init二、依赖库 Debian系(包括ubuntu)系统下安装依赖的方法: #除了…

【考研数学】《660》+《880》高分搭配方法

📝《660题》和《880题》高效刷题方法 1️⃣做题要有针对性,不要为了做题而做题 💪660和880题虽然多,但是你不用全都做完,你可以把它当成是题源,里面的每一道题都很经典,如果搞懂一道&#xff…

AcWing 796. 子矩阵的和——算法基础课题解

AcWing 796. 子矩阵的和 题目描述 输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。 对于每个询问输出子矩阵中所有数的和。 输入格式 第一行包含三个整数 n&…

Leetcode刷题之消失的数字(C语言版)

Leetcode刷题之消失的数字(C语言版) 一、题目描述二、题目解析 一、题目描述 数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗? 注意:本题相对书上原题稍作…

大数据------JavaWeb------JDBC(完整知识点汇总)

JDBC 定义 全称为Java数据库连接(Java DataBase Connectivity):是使用java语句来操作所有关系型数据库的一套API JDBC本质 它是官方定义的一套操作所有关系型数据库的规则(即接口),各个数据库厂商会去实现…

使用Nodejs + express连接数据库mongoose

文章目录 先创建一个js文档安装 MongoDB 驱动程序:引入 MongoDB 模块:设置数据库连接:新建一个表试试执行数据库操作:关闭数据库连接: 前面需要准备的内容可看前面的文章: Express框架搭建项目 node.js 简单…

企业官网为什么需要配置独享IP?

什么是独享IP和共享IP?独享IP有什么优点?企业网站配置独享IP有什么优势呢?下面给大家简单介绍下。 什么是独享IP和共享IP? 首先,让我们了解IP(Internet Protocol)的概念。IP地址是一个数字串&…

CSS水波纹效果

效果图&#xff1a; 1.创建一个div <div class"point1" click"handlePoint(1)"></div> 2.设置样式 .point1{width: 1rem;height: 1rem;background: #2ce92f;position: absolute;border-radius: 50%;z-index: 999;cursor: pointer;} 3.设置伪…

电子元器件线上交易商城搭建的价值和必要性-加速度jsudo

随着科技的飞速发展&#xff0c;电子元器件行业正迎来前所未有的变革。为了满足市场对于电子元器件采购的便捷性、高效性和多样性的需求&#xff0c;电子元器件商城的开发显得尤为重要。本文将探讨电子元器件商城开发的重要性、主要功能以及它如何助力行业发展。 电子元器件商城…

实用工具系列-ADB使用方式

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;WPS二次开发QQ群:250325397&#xff09;&#xff0c;摸鱼吹牛嗨起来&#xff0…

C++ | Leetcode C++题解之第22题括号生成

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<string> res; //记录答案 vector<string> generateParenthesis(int n) {dfs(n , 0 , 0, "");return res;}void dfs(int n ,int lc, int rc ,string str){if( lc n && rc n…

如何应对Android面试官 -> ActivityManagerService 是怎么启动的?

前言 本章主要讲解下 什么是 AMS&#xff0c;以及它是如何启动的&#xff1b; SystemServer SystemServer 通过按下电源键接通电源之后就会启动 BootRoom&#xff0c;BootRoom 就会拉起一个 BootLoader 程序&#xff0c;此程序会拉起 Linux Kernel「系统内核」&#xff0c;我们…

小程序如何通过把动态数据值传入到css文件中控制样式

场景&#xff1a;动态改变一个模块的高度 一、常用解决方法&#xff1a;行内样式绑值&#xff0c;或者动态class来传递 <viewclass"box":style"height: ${boxHeight}px">我是一个动态高度的box,我的高度是{{boxHeight}}px </view>二、高度传…

关于Linux下的进程替换(进程篇)

目录 进程替换是什么&#xff1f; 进程替换需要怎样操作&#xff1f; 替换函数 命名理解 不创建子进程进行进程替换 关于替换程序时的写时拷贝 fork创建子进程进行替换 函数1&#xff1a;execl 函数2&#xff1a;execv 函数3&#xff1a;execlp 函数4&#xff1a;execvp…

python-study-day1-(病人管理系统-带sql)

MainWindow代码 from tkinter import * from tkinter import messagebox from tkinter.ttk import Comboboxclass MianWindow(Frame):def __init__(self, masterNone):super().__init__(master, padx30, pady20)self.flag 0self.pack(expandTrue, fillBOTH)self.id StringVa…

vue3:菜单、标签页和面包屑联动效果

文章目录 1.整体思路2.实现过程 概要 提示&#xff1a;这里可以添加技术概要 例如&#xff1a; openAI 的 GPT 大模型的发展历程。 1.整体思路 在之前做的后台项目中&#xff0c;菜单、标签页和面包屑之间的联动&#xff0c;自己都是通过在路由前置守卫中&#xff0c;定义b…

铭飞SQL注入严重信息泄露【附POC】

感谢您抽出 阅读本文 MCMS是一个<完整开源的Java CMS&#xff01;基于SpringBoot 2架构&#xff0c;前端基于vue、element ui。MCMS存在SQL注入漏洞&#xff0c;攻击者可通过该漏洞获取数据库敏感信息等。目前厂商暂未发布修复措施解决此安全问题&#xff0c;建议使用此软件…