第N4周:中文文本分类

news2024/11/20 19:32:04
  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊

一、预备知识

中文文本分类和英文文本分类都是文本分类,为什么要单独拎出来个中文文本分类呢?
在自然语言处理(NLP)领域,中文处理和英文处理之间存在一些显著的区别,主要由于两种语言的语法、结构和表达方式的不同。以下是一些主要的区别:

  1. 分词(Tokenization)
    • 英文处理通常以空格和标点符号作为分词的天然界限,因此分词相对简单。
    • 中文处理则复杂得多,因为中文书写时词与词之间没有明显的分隔符。中文分词需要识别词语的边界,这通常涉及到复杂的算法和大量的词典资源。
  2. 词性标注(Part-of-Speech Tagging)
    • 英文的词性变化相对规则,例如通过词尾变化可以区分动词的时态和语态。
    • 中文词性标注需要识别每个词的词性,但由于中文词语没有明显的形态变化,这通常需要依赖于上下文信息。
  3. 句法分析(Parsing)
    • 英文的句法结构通常由明确的词汇形态变化和固定的词序来表达。
    • 中文句法结构更多地依赖于词序和功能词来表示,因此中文的句法分析需要考虑到这些因素。
  4. 语义分析(Semantic Analysis)
    • 英文的语义可以通过词汇的词根和词缀来推断。
    • 中文语义分析则需要更多地依赖于词汇的组合和上下文,因为中文词语往往具有多个意义。
  5. 机器翻译(Machine Translation)
    • 英文和其他印欧语系的语言之间由于语法结构相似,机器翻译相对容易。
    • 中文与英文之间的机器翻译更为复杂,需要处理语言结构的不对齐和文化差异。
  6. 语音和发音
    • 英文处理通常涉及到音标和发音规则。
    • 中文处理则涉及到声调,声调的变化会改变词语的意义。
  7. 上下文依赖性
    • 中文更加依赖于上下文来解析词语和句子的意义,因为一个词语可能有多个意思,具体意思需要根据上下文来确定。
    • 英文虽然也有多义词,但上下文依赖性通常没有中文那么强。
  8. 资源和工具
    • 英文NLP有大量的预训练模型、工具和资源可供使用。
    • 中文NLP虽然也有不少资源,但相比英文来说还是较少,尤其是在开源领域。

二、准备工作

1.导入必要的包,检查设备

import torch
import torch.nn as nn
import torchvision
from torchvision import transforms,datasets
import os,PIL,pathlib,warnings

warnings.filterwarnings("ignore")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device

输出
device(type=‘cuda’)

2.导入数据

import pandas as pd

train_data = pd.read_csv('./train.csv', sep='\t', header=None)
train_data.head()

输出
在这里插入图片描述

3.构建数据集迭代器

def coustom_data_iter(texts, labels):
    for x, y in zip(texts, labels):
        yield x,y

train_iter = coustom_data_iter(train_data[0].values[:], train_data[1].values[:])

三、数据预处理

1.构建词典

from torchtext.data.utils import get_tokenizer
from torchtext.vocab import build_vocab_from_iterator
import jieba

# 中文分词方法
tokenizer = jieba.lcut

def yield_tokens(data_iter):
    for text,_ in data_iter:
        yield tokenizer(text)

vocab = build_vocab_from_iterator(yield_tokens(train_iter), specials=["<unk>"])
vocab.set_default_index(vocab["<unk>"]) # 设置默认索引,如果找不到单词,则会选择默认索引

输出

在这里插入图片描述

2.抽样检查

vocab(['你','和','他们','一起','上','空调','播放','农历'])

[18, 84, 1752, 444, 146, 43, 4, 44]

label_name = list(set(train_data[1].values[:]))
print(label_name) #打印标签以确认

[‘TVProgram-Play’, ‘Radio-Listen’, ‘Video-Play’, ‘HomeAppliance-Control’, ‘Weather-Query’, ‘Audio-Play’, ‘FilmTele-Play’, ‘Calendar-Query’, ‘Alarm-Update’, ‘Travel-Query’, ‘Music-Play’, ‘Other’]

lambda 表达式的语法为: lambda arguments: expression
其中 arguments 是函数的参数,可以有多个参数,用逗号分隔。expression 是一个表达式,它定义了函数的返回值。
text_pipeline函数:将原始文本数据转换为整数列表,使用之前构建的vocb词表和tokenizer分词器函数。具体来说,它接受一个字符串x作为输入,首先使用tokenizer将其分词,然后将每个词在vocb词表中的索引放入一个列表中返回。
label_pipeline函数:将原始标签数据转换为整数,并使用一个字符xx作为输入,并使用 label_name.index(x) 方法获取 x 在 label_name 列表中的索引作为输出。
lambda表达式通常用于数据预处理阶段,将文本数据转换为可以输入到模型中的索引序列

text_pipeline = lambda x: vocab(tokenizer(x))
label_pipeline = lambda x: label_name.index(x)
print(text_pipeline('我看见看和平精英上战神必备技巧的游戏视频'))
print(label_pipeline('Video-Play'))

[2, 9317, 13, 973, 1079, 146, 7724, 7574, 7793, 1, 186, 28]
2

3.生成数据批次和迭代器

from torch.utils.data import DataLoader

def collate_batch(batch):
    label_list, text_list, offsets = [], [], [0]
    
    for (_text,_label) in batch:
    
        label_list.append(label_pipeline(_label))
        
        processed_text = torch.tensor(text_pipeline(_text), dtype=torch.int64)
        text_list.append(processed_text)
        
        offsets.append(processed_text.size(0))
        
    label_list = torch.tensor(label_list, dtype=torch.int64)
    text_list = torch.cat(text_list)
    offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)  
    
    return text_list.to(device), label_list.to(device), offsets.to(device)

dataloader = DataLoader(train_iter,
                       batch_size=4,
                       shuffle=False,
                       collate_fn=collate_batch)

这段代码定义了一个collate_batch函数,用于将一批次的文本数据和标签数据整理成一个适合模型训练的格式,并创建一DataLoader对象来迭代处理这些批次数据。

  1. from torch.utils.data import DataLoader 这行代码从torch.utils.data模块中导入DataLoader类,DataLoader是一个迭代器,它允许我们以批量形式加载数据集,并提供数据混洗等功能。
  2. def collate_batch(batch): 这行代码定义了一个名为collate_batch的函数,它接受一个参数batch,这个batch是一个列表,包含了多个文本和标签对。
  3. label_list, text_list, offsets = [], [], [0] 这行代码初始化了三个列表:label_list用于存储标签,text_list用于存储处理后的文本数据,offsets用于存储每个文本数据的偏移量,用于后续的打包操作。
  4. for (_text,_label) in batch: 这个循环遍历batch中的每个元素,每个元素是一个包含文本和标签的元组。
  5. label_list.append(label_pipeline(_label)) 这行代码将每个标签通过label_pipeline函数处理后添加到label_list中。label_pipeline是一个未定义的函数,应该是用来将标签数据转换为模型可接受的格式。
  6. processed_text = torch.tensor(text_pipeline(_text), dtype=torch.int64) ,这行代码将每个文本通过text_pipeline函数处理后转换为PyTorch张量。text_pipeline是一个之前定义的lambda表达式,用于将文本转换为词汇索引列表。
  7. text_list.append(processed_text) 这行代码将处理后的文本张量添加到text_list中。
  8. offsets.append(processed_text.size(0)) 这行代码计算每个文本的长度,并将其作为偏移量添加到offsets列表中。
  9. label_list = torch.tensor(label_list, dtype=torch.int64) 这行代码将label_list列表转换为PyTorch张量。
  10. text_list = torch.cat(text_list): 这行代码使用torch.cat函数将text_list中的所有文本张量沿第一个维度(维度0)拼接成一个大的张量。
  11. offsets = torch.tensor(offsets[:-1]).cumsum(dim=0)这行代码将offsets列表转换为PyTorch张量,并计算其累加和(cumulative sum),这样每个偏移量就表示了对应文本在拼接后张量中的起始位置。
  12. return text_list.to(device), label_list.to(device), offsets.to(device) 这行代码将处理后的文本、标签和偏移量张量移动到定义的设备(如CPU或GPU)上,并作为函数的返回值。
  13. dataloader = DataLoader(train_iter, batch_size=4, shuffle=False, collate_fn=collate_batch)这行代码创建了一个DataLoader对象dataloader,它使用train_iter作为数据源,batch_size设置为4,shuffle设置为False表示不打乱数据顺序,collate_fn设置为collate_batch表示使用自定义的collate_batch函数来整理每个批次的数据。

四、模型构建

1.搭建模型

from torch import nn

class TextClassificationModel(nn.Module):
    def __init__(self, vocab_size, embed_dim, num_class):
        super(TextClassificationModel, self).__init__()
        self.embedding = nn.EmbeddingBag(vocab_size, embed_dim, sparse=False)
        self.fc = nn.Linear(embed_dim, num_class)
        self.init_weights()

    def init_weights(self):
        initrange = 0.5
        self.embedding.weight.data.uniform_(-initrange, initrange)  
        self.fc.weight.data.uniform_(-initrange, initrange)
        self.fc.bias.data.zero_() 
        
    def forward(self, text, offsets):
        embedded = self.embedding(text, offsets)
        return self.fc(embedded)

这段代码定义了一个用于文本分类的神经网络模型TextClassificationModel,它是一个基于PyTorch的nn.Module

  1. from torch import nn 这行代码从torch库中导入nn模块,这个模块包含了构建神经网络所需的各种层和函数。
  2. class TextClassificationModel(nn.Module): 这行代码定义了一个名为TextClassificationModel的新类,它继承自nn.Module。这意味着这个类会拥有nn.Module的所有方法和属性,包括神经网络的前向传播方法。
  3. def __init__(self, vocab_size, embed_dim, num_class): 这行代码定义了类的初始化方法__init__,它接受三个参数:vocab_size(词汇表的大小),embed_dim(嵌入层的维度),num_class(分类任务的类别数)。
  4. super(TextClassificationModel, self).__init__() 这行代码调用父类nn.Module的初始化方法。
  5. self.embedding = nn.EmbeddingBag(vocab_size, embed_dim, sparse=False) 这行代码创建了一个nn.EmbeddingBag对象,它是一个特殊的嵌入层,可以处理可变长度的序列数据,并且会自动计算所有嵌入向量的平均值。sparse=False表示不使用稀疏权重。
  6. self.fc = nn.Linear(embed_dim, num_class) 这行代码创建了一个全连接层fc,它将嵌入层的输出映射到分类的类别数。
  7. self.init_weights() 这行代码调用了类中定义的init_weights方法,用于初始化模型的权重。
  8. def init_weights(self): 这行代码定义了init_weights方法,用于初始化嵌入层和全连接层的权重。
  9. initrange = 0.5 这行代码定义了初始化范围initrange,用于权重初始化。
  10. self.embedding.weight.data.uniform_(-initrange, initrange)这行代码将嵌入层的权重初始化为在-initrangeinitrange之间的均匀分布。

这段代码是在Pytorch框架下用于初始化神经网络的词嵌入层(embedding layer)权重的一种方法。这里使用了均匀分布的随机值来初始化权重,其作用如下:

  1. self.embedding.weight_data.uniform(-irange, irange): 这是神经网络中的词嵌入层(embedding layer)。词嵌入层的作用是将离散的单词表示为固定大小的连续向量(通常为整数索引)。这些向量捕捉了单词之间的语义关系,并作为网络的输入。
  2. self.embedding.weight_data: 它是词嵌入层的权重矩阵,它的形状为(vocab_size, embedding_dim),其中vocb_size是词汇表的大小,embedding_dim是维度参数。
  3. self.embedding.weight_data.uniform(-irange, irange): 这是一个原地操作(in-place operation),用于将权重矩阵的一个均匀分布进行初始化。均匀分布的分布范围由参数irange决定。
  4. uniform(-irange, irange), intrance(irange): 356度深度学习训练营 通过这种方式初始化词嵌入层的权重,可以使得模型在训练开始时具有一定程度的随机性,有助于避免梯度消失或梯度爆炸等问题。在训练过程中,这些权重将通过优化算法不断更新,以捕捉到更好的单词表示。
  1. self.fc.weight.data.uniform_(-initrange, initrange)这行代码将全连接层的权重初始化为在-initrangeinitrange之间的均匀分布。
  2. self.fc.bias.data.zero_()这行代码将全连接层的偏置初始化为0。
  3. def forward(self, text, offsets): :这行代码定义了模型的前向传播方法forward,它接受文本数据和偏移量作为输入。
  4. embedded = self.embedding(text, offsets) :这行代码使用嵌入层处理文本数据,offsets用于指定每个序列的开始位置。
  5. return self.fc(embedded):这行代码将嵌入层的输出传递给全连接层,并返回最终的分类结果。

2.初始化模型

num_class = len(label_name)
vocab_size = len(vocab)
em_size = 64
model = TextClassificationModel(vocab_size, em_size, num_class).to(device)

3.定义训练和评估的函数

import time
def train(dataloader):
    model.train()  # 切换为训练模式
    total_acc, train_loss, total_count = 0, 0, 0
    log_interval = 50
    start_time = time.time()
    
    for idx, (text, label, offsets) in enumerate(dataloader):
        predicted_label = model(text, offsets)
        optimizer.zero_grad()
        
        # grad属性归零
        loss = criterion(predicted_label, label)  # 计算网络输出和真实值之间的差距,label为真实值
        loss.backward()
        # 反向传播
        torch.nn.utils.clip_grad_norm(model.parameters(), 0.1)  # 梯度裁剪
        optimizer.step()  # 每一步自动更新
        
        # 记录acc与loss
        total_acc += (predicted_label.argmax(1) == label).sum().item()
        train_loss += loss.item()
        total_count += label.size(0)
        if idx % log_interval == 0 and idx > 0:
            elapsed = time.time() - start_time
            print('| epoch {:4d} | {:4d}/{:4d} batches | '
                  'train_acc {:4.3f} | train_loss {:4.5f} |'.format(epoch, idx, len(dataloader),
                                                                    total_acc / total_count, train_loss / total_count))
    total_acc, train_loss, total_count = 0, 0, 0
    start_time = time.time()
    
def evaluate(dataloader):
    model.eval()  # 切换为测试模式
    total_acc, val_loss, total_count = 0, 0, 0
    with torch.no_grad():
        for idx, (text, label, offsets) in enumerate(dataloader):
            predicted_label = model(text, offsets)
            loss = criterion(predicted_label, label)  # 计算loss值
            # 记录测试数据
            total_acc += (predicted_label.argmax(1) == label).sum().item()
            val_loss += loss.item()
            total_count += label.size(0)
    return total_acc / total_count, val_loss / total_count
  1. import time :导入time模块,用于计算训练和评估过程中的时间。
  2. def train(dataloader): 定义一个名为train的函数,它接受一个名为dataloader的参数,这个参数应该是一个数据加载器,用于提供批量数据。
  3. model.train() 将模型设置为训练模式,这对于一些层(如dropout和batch normalization)是必要的。
  4. total_acc, train_loss, total_count = 0, 0, 0 初始化累加器,用于记录整个训练过程中的准确率、损失和总样本数。
  5. log_interval = 50 定义一个日志打印间隔,每50个批次打印一次训练状态。
  6. start_time = time.time() 记录训练开始的时间。
  7. for idx, (text, label, offsets) in enumerate(dataloader): 遍历dataloader中的每个批次,idx是批次的索引,(text, label, offsets)是每个批次的数据。
  8. predicted_label = model(text, offsets) 使用模型对输入的textoffsets进行前向传播,得到预测的标签。
  9. optimizer.zero_grad() 清空模型的梯度,为下一次反向传播做准备。
  10. loss = criterion(predicted_label, label)计算预测标签和真实标签之间的损失。
  11. loss.backward()对损失进行反向传播,计算模型参数的梯度。
  12. torch.nn.utils.clip_grad_norm(model.parameters(), 0.1)对模型的梯度进行裁剪,防止梯度爆炸。

torch.nn.utils.clip_grad_norm(model_parameters(), 1)是一个PyTorch函数,用于在训练神经网络时限制梯度的大小。这种操作被称为梯度裁剪(gradient clipping),可以防止梯度爆炸问题,从而提高神经网络的稳定性和性能。
在这个函数中:

  • model_parameters()表示模型的所有参数。对于一个神经网络,参数通常包括权重和偏置项。
  • 0.1是一个指定的阈值,表示梯度的最大范数(L2范数)。如果计算出的梯度范数超过这个阈值,梯度会被缩放,使其范数等于阈值。梯度裁剪的主要目的是防止梯度爆炸。梯度爆炸通常发生在训练深度神经网络时,尤其是在处理长序列数据的传播网络(RNN)中。当梯度爆炸时,参数可能会变得非常大,导致模型无法收敛或出现数值不稳定。通过限制梯度的大小,梯度裁剪有助于解决这些问题,使模型训练变得更加稳定。
  1. optimizer.step()使用优化器更新模型的参数。
  2. total_acc += (predicted_label.argmax(1) == label).sum().item()累加本次批次的准确率。
  3. train_loss += loss.item()累加本次批次的损失。
  4. total_count += label.size(0)累加本次批次的样本数。
  5. if idx % log_interval == 0 and idx > 0:如果达到日志打印间隔,打印当前的训练状态。
  6. elapsed = time.time() - start_time计算自训练开始以来的时间。
  7. print('| epoch {:4d} | {:4d}/{:4d} batches | train_acc {:4.3f} | train_loss {:4.5f} |'.format(...)打印训练进度、准确率和损失。
  8. total_acc, train_loss, total_count = 0, 0, 0重置累加器,为下一个训练周期做准备。
  9. start_time = time.time()重置开始时间,为下一个训练周期做准备。
  10. def evaluate(dataloader):定义一个名为evaluate的函数,用于评估模型的性能。
  11. model.eval()将模型设置为评估模式,这对于一些层(如dropout和batch normalization)是必要的。
  12. total_acc, val_loss, total_count = 0, 0, 0初始化累加器,用于记录整个评估过程中的准确率、损失和总样本数。
  13. with torch.no_grad():在这个上下文中,所有的计算都不会计算梯度,这样可以节省内存和计算资源。
  14. for idx, (text, label, offsets) in enumerate(dataloader): 遍历dataloader中的每个批次。
  15. predicted_label = model(text, offsets)使用模型进行前向传播,得到预测的标签。
  16. loss = criterion(predicted_label, label)计算预测标签和真实标签之间的损失。
  17. total_acc += (predicted_label.argmax(1) == label).sum().item()累加本次批次的准确率。
  18. val_loss += loss.item()累加本次批次的损失。
  19. total_count += label.size(0)累加本次批次的样本数。
  20. return total_acc / total_count, val_loss / total_count返回整个评估过程中的平均准确率和平均损失。 总的来说,train函数用于训练模型,而evaluate函数用于评估模型的性能。这两个函数都遍历数据加载器中的所有批次,并累加准确率和损失,最后打印或返回这些统计信息。

五、训练模型

1.拆分数据集并运行模型

from torch.utils.data.dataset import random_split
from torchtext.data.functional import to_map_style_dataset

# 超参数
EPOCHS = 10  
LR = 5      
BATCH_SIZE = 32

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.1)
total_accu = None

# 构建数据集
train_iter = coustom_data_iter(train_data[0].values[:], train_data[1].values[:])
train_dataset = to_map_style_dataset(train_iter)
split_train_, split_valid_= random_split(train_dataset, [int(len(train_dataset) * 0.8), int(len(train_dataset) * 0.2)])
train_dataloader = DataLoader(split_train_, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_batch)
valid_dataloader = DataLoader(split_valid_, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_batch)

for epoch in range(1, EPOCHS + 1):
    epoch_start_time = time.time()
    train(train_dataloader)
    val_acc, val_loss = evaluate(valid_dataloader)
    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']
    if total_accu is not None and total_accu > val_acc:
        scheduler.step()
    else:
        total_accu = val_acc
    print('-' * 69)
    print('| epoch {:1} | time:{:4.2f} '
          '| valid_acc {:4.3f} | valid_loss {:4.3f} | lr {:6f} '.format(epoch, time.time() - epoch_start_time, val_acc, val_loss, lr))
    
    print('-' * 69)

torchtext.data.functional_map style dataset函数的作用是将一个迭代式的数据集(iterable-style dataset)转换为映射为索引的数据集(Map-style dataset)。这个转换使得我们可以通过索引(例如:整数)更方便地访问数据集中的元素。
在PyTorch中,数据集可以分为两种类型:Iterable-style和Map-style。Iterable-style数据集实现了一个迭代器方法,可以用于遍历数据集中的元素,但不支持通过索引访问。而Map-style数据集实现了getitem()和len()方法,可以直接通过索引访问特定元素,并能获取数据集的大小。
TorchText是PyTorch的一个扩展库,专注于处理文本数据。torchtext.data.functional_map_style dataset函数可以帮助我们将一个Iterable-style数据集转换为一个易于操作的Map-style数据集。这样,我们可以通过索引直接访问数据集中特定的样本,从而简化了训练、验证和测试过程中的数据处

在这里插入图片描述

test_acc, test_loss = evaluate(valid_dataloader)
print('模型准确率为:{:5.4f}'.format(test_acc))

模型准确率为:0.8971

2.测试数据

def predict(text, text_pipeline): 
    with torch.no_grad():
        text = torch.tensor(text_pipeline(text))
        output = model(text, torch.tensor([0]))
        return output.argmax(1).item()
ex_text_str1 = "随便播放一首专辑阁楼里的佛里的歌"
ex_text_str2 = "还有双鸭山到淮阴的汽车票吗13号的"
model = model.to("cpu")
print("该文本的类别是:%s" % label_name[predict(ex_text_str1,text_pipeline)])
print("该文本的类别是:%s" % label_name[predict(ex_text_str2,text_pipeline)])

该文本的类别是:Music-Play
该文本的类别是:Travel-Query

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

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

相关文章

SickOS1.1 - Shellshock原理和利用过程精讲

SickOS1.1的另一种思路&#xff1b;用另一种方法打透这台机器 Nikto扫描 正常都是-h扫描&#xff1b;有代理就用-useproxy 指向的代理ip:端口 nikto -h 192.168.218.157 -useproxy 192.168.218.157:3128apache版本&#xff0c;有点低&#xff0c;现在都是2.4.54版本了&#x…

PDF批量加水印 与 去除水印实践

本文主要目标是尝试去除水印&#xff0c;但是为了准备测试数据&#xff0c;我们需要先准备好有水印的pdf测试文件。 注意&#xff1a;本文的去水印只针对文字悬浮图片悬浮两种特殊情况&#xff0c;即使是这两种情况也不代表一定都可以去除水印。 文章目录 批量添加透明图片水印…

OpenStreetMap部署(OSM)

参考&#xff1a;https://github.com/openstreetmap/openstreetmap-website/blob/master/DOCKER.md OpenStreeMap 部署 操作系统建议使用 Ubuntu 22 版本 安装 Docker # 更新软件包索引&#xff1a; sudo apt-get update # 允许APT使用HTTPS&#xff1a; sudo apt-get inst…

TypeScript的never类型的妙用

never类型介绍 在 TypeScript 中&#xff0c;"never" 是一个表示永远不会发生的值类型。 使用场景 "never" 类型通常用于以下几种情况&#xff1a; 1、函数返回类型&#xff1a;当一个函数永远不会返回任何值&#xff08;比如抛出异常或者无限循环&…

使用 MDC 实现日志链路跟踪,包教包会!

在微服务环境中&#xff0c;我们经常使用 Skywalking、Spring Cloud Sleut 等去实现整体请求链路的追踪&#xff0c;但是这个整体运维成本高&#xff0c;架构复杂&#xff0c;本次我们来使用 MDC 通过 Log 来实现一个轻量级的会话事务跟踪功能&#xff0c;需要的朋友可以参考一…

数据库与缓存⼀致性⽅案

数据库与缓存⼀致性⽅案 1、背景2、数据⼀致性⽅案设计3、数据⼀致性⽅案流程图4、关键代码4.1、 处理数据⼀致性的消息队列⼊⼝4.2、数据⼀致性配置的常量信息 1、背景 现有的业务场景下&#xff0c;都会涉及到数据库以及缓存双写的问题&#xff0c;⽆论是先删除缓存&#xf…

2024年度CCF-阿里云瑶池科研基金正式发布

2024年度CCF-阿里云瑶池科研基金正式发布 截止时间&#xff1a;2024年7月1日24:00&#xff08;北京时间&#xff09; 欢迎CCF会员积极申报 “CCF-阿里云瑶池科研基金”由CCF与阿里云计算有限公司于2024年联合设立&#xff0c;专注于数据库领域&#xff0c;旨在为领域学者提供…

FarmersWorld农民世界源码开发:0撸卷轴+潮玩模式

一、引言 随着科技的发展&#xff0c;游戏产业日益壮大&#xff0c;一种新型的游戏形式——零撸游戏应运而生。本文将深入探讨FarmersWorld农民世界源码开发&#xff0c;以其独特的0撸卷轴潮玩模式&#xff0c;为玩家带来全新的游戏体验。 二、源码开发的专业性和深度 Farmer…

找出字符串中出现最多次数的字符以及出现的次数

str.charAt(i) 是JavaScript中获取字符串中特定位置字符的方法&#xff0c;表示获取当前的字符。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

进入新公司有焦虑感怎么办?

前因 前两天技术交流群里有童鞋问了一个很有意思的问题&#xff0c;他问如何克服进入新公司的焦虑感&#xff1f;很多热心的童鞋都纷纷支招&#xff0c;比如 “主动干活”、“专注干活”、“让时间冲淡焦虑感”、……等等&#xff0c;这些都很有道理&#xff0c;不过&#xff…

win11右键二级菜单恢复成win10一级菜单

winr输入“cmd”回车&#xff0c;打开cmd窗口&#xff0c;输入如下命令&#xff0c;并回车。reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve提示cuccessfully&#xff0c;表示操作成功。重启电脑即可。 如下…

NEJM新英格兰医学期刊文献在家如何查阅下载

今天收到的求助文献中有一篇是NEJM新英格兰医学期刊中的一篇文献&#xff0c;篇名“Osimertinib after Chemoradiotherapy in Stage III EGFR -Mutated NSCLC” 首先我们先简单了解一下NEJM新英格兰医学期刊&#xff1a; NEJM新英格兰医学期刊&#xff1a;New England Journa…

排序进阶----快速排序

当我们写了插入和希尔排序后&#xff0c;我们就应该搞更难的了吧。大家看名字就知道我们这篇博客的内容了吧。而且从名字上来看。快速排序就很快吧。那么为什么这个排序怎么能叫快速排序啊。我们希尔排序不是很快嘛。那么我们的快速排序肯定是有特殊之处嘞。不然这就太自负了。…

Qt Group宣布更新许可协议

本文翻译自&#xff1a;Qt Group Launches Updates of License Agreements 原文作者&#xff1a;Qt Group产品管理总监Santtu Ahonen 为了简化Qt Group的许可协议并提升整体的可读性&#xff0c;我们对商业合同文档的结构进行了更新。这一变更对Qt Group许可其商业产品和服务的…

SpringCache 缓存 - @Cacheable、@CacheEvict、@CachePut、@Caching、CacheConfig 以及优劣分析

目录 SpringCache 缓存 环境配置 1&#xff09;依赖如下 2&#xff09;配置文件 3&#xff09;设置缓存的 value 序列化为 JSON 格式 4&#xff09;EnableCaching 实战开发 Cacheable CacheEvict CachePut Caching CacheConfig SpringCache 的优势和劣势 读操作…

出行预测:端午打车需求将上涨31%,滴滴发放超2亿司机补贴

作为上半年的“收官”小长假&#xff0c;端午假期接棒“五一”的出行热度&#xff0c;中短途周边游持续升温&#xff0c;海滨旅行、龙舟民俗体验成为新的出行看点。 滴滴出行预测&#xff0c;端午节当天&#xff08;6月10日&#xff09;打车需求将同比去年上涨约31%。今年端午…

因子区间[牛客周赛44]

思路分析: 我们可以发现125是因子个数的极限了,所以我们可以用二维数组来维护第几个数有几个因子,然后用前缀和算出来每个区间合法个数,通过一个排列和从num里面选2个 ,c num 2 来计算即可 #include<iostream> #include<cstring> #include<string> #include…

长虹智能电视55D3P(机芯:ZLH74GiR2G)海思平台固件解析打包

一、使用Hitool打包固件 接上一篇&#xff0c;尝试使用HITOOL打包固件 长虹55D3P海思平台固件破解-CSDN博客 参考ZNDS HItool备份固件&#xff1a;【玩机必看】海思机顶盒备份线刷包 制作分区表xml文件_ZNDS刷机/救砖_ZNDS HITOOL下载&#xff1a;https://cloud.189.cn/web/…

关于信号翻转模块(sig_flag_mod)的实现

关于信号翻转模块(sig_flag_mod)的实现 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于信号翻转模块(sig_flag_mod)的实现一、引言二、实现信号翻转模块的方法&#xff08;1&#xff09;输入接口&#xff08;2&#xff09;输出接口&…

如何跨渠道分析销售数据 - 7年制造业销售经验小结

如何跨渠道分析销售数据 - 7年制造业销售经验小结&#xff08;1&#xff09; 【前言】 在我过去7年销售工作生涯中&#xff0c;从第一年成为公司销冠后&#xff0c;我当时的确自满的一段时间&#xff0c;认为自己很了不起。但是第一年的销售业绩并没有拿到提成&#xff0c;最…