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

PyTorch 基础学习(10)- Transformer

系列文章:
《PyTorch 基础学习》文章索引

介绍

Transformer模型是近年来在自然语言处理(NLP)领域中非常流行的一种模型架构,尤其是在机器翻译任务中表现出了优异的性能。与传统的循环神经网络(RNN)不同,Transformer模型完全基于注意力机制,避免了序列处理中的长距离依赖问题。本教程将通过一个简单的实例,详细讲解如何在PyTorch中实现一个基于Transformer的机器翻译模型。

Transformer的原理简介

Transformer模型由Vaswani等人在2017年提出,其核心思想是利用注意力机制来捕捉输入序列中的长程依赖关系。模型主要包括两个模块:编码器(Encoder)和解码器(Decoder)。每个模块由多个层(Layer)堆叠而成,每一层又包含多个子层(Sub-layer),如自注意力机制(Self-Attention)、前馈神经网络(Feed-Forward Neural Network)等。

1. 自注意力机制(Self-Attention)

自注意力机制是Transformer的核心,主要用于计算输入序列中各元素之间的相互依赖关系。通过自注意力机制,模型可以在每一步中考虑到整个序列的信息,而不是仅仅依赖于固定的上下文窗口。

2. 多头注意力机制(Multi-Head Attention)

多头注意力机制是对自注意力机制的扩展,通过引入多个注意力头(Attention Heads),模型可以在不同的子空间中独立地计算注意力,从而捕捉到输入序列中更多的特征。

3. 前馈神经网络(Feed-Forward Neural Network)

在每个编码器和解码器层中,注意力机制后接一个前馈神经网络。该网络在每个时间步上独立应用于序列中的每一个位置。

4. 残差连接与层归一化(Residual Connection & Layer Normalization)

为了缓解梯度消失的问题,Transformer模型在每个子层之间使用了残差连接,并在每个子层后使用层归一化。

实例代码及讲解

下面我们将通过一个简单的示例代码,详细讲解如何在PyTorch中实现一个基于Transformer的句子推理。

1. 导入必要的库

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torch.nn.utils.rnn import pad_sequence
import numpy as np

2. 定义数据集类

class TranslationDataset(Dataset):def __init__(self, source_sentences, target_sentences, src_vocab, tgt_vocab):self.source_sentences = source_sentencesself.target_sentences = target_sentencesself.src_vocab = src_vocabself.tgt_vocab = tgt_vocabdef __len__(self):return len(self.source_sentences)def __getitem__(self, idx):src = [self.src_vocab[word] for word in self.source_sentences[idx].split()]tgt = [self.tgt_vocab[word] for word in self.target_sentences[idx].split()]return torch.tensor(src), torch.tensor(tgt)
  • TranslationDataset类继承自Dataset,用于处理机器翻译任务中的数据集。
  • __getitem__方法根据索引idx返回源句子和目标句子的张量表示。

3. 定义collate_fn函数

def collate_fn(batch):src_batch, tgt_batch = zip(*batch)src_batch = pad_sequence(src_batch, padding_value=0, batch_first=True)tgt_batch = pad_sequence(tgt_batch, padding_value=0, batch_first=True)return src_batch, tgt_batch
  • collate_fn函数用于将一个批次的数据进行填充,使得每个批次的源句子和目标句子长度一致。

4. 定义Transformer模型

class TransformerModel(nn.Module):def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6,dim_feedforward=2048, dropout=0.1):super(TransformerModel, self).__init__()self.embedding_src = nn.Embedding(src_vocab_size, d_model)self.embedding_tgt = nn.Embedding(tgt_vocab_size, d_model)self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward,dropout)self.fc_out = nn.Linear(d_model, tgt_vocab_size)self.d_model = d_modeldef forward(self, src, tgt):src = self.embedding_src(src) * np.sqrt(self.d_model)tgt = self.embedding_tgt(tgt) * np.sqrt(self.d_model)src = src.permute(1, 0, 2)tgt = tgt.permute(1, 0, 2)output = self.transformer(src, tgt)output = self.fc_out(output)return outputdef generate(self, src, max_len, sos_idx):self.eval()src = self.embedding_src(src) * np.sqrt(self.d_model)src = src.permute(1, 0, 2)  # [sequence_length, batch_size, d_model]memory = self.transformer.encoder(src)# 初始化解码器输入,开始标记ys = torch.ones(1, 1).fill_(sos_idx).type(torch.long).to(src.device)for i in range(max_len - 1):tgt = self.embedding_tgt(ys) * np.sqrt(self.d_model)tgt_mask = nn.Transformer.generate_square_subsequent_mask(tgt.size(0)).to(src.device)out = self.transformer.decoder(tgt, memory, tgt_mask=tgt_mask)out = self.fc_out(out)prob = out[-1, :, :].max(dim=-1)[1]ys = torch.cat([ys, prob.unsqueeze(0)], dim=0)if prob == 2:  # <eos> token indexbreakreturn ys.transpose(0, 1)
  • TransformerModel类继承自nn.Module,封装了Transformer模型。
  • forward方法定义了模型的前向传播逻辑,包括对源句子和目标句子进行嵌入、通过Transformer层处理,以及通过线性层输出预测结果。
  • generate方法用于推理,生成翻译结果。

5. 训练和评估函数

def train(model, dataloader, optimizer, criterion, num_epochs=10):model.train()for epoch in range(num_epochs):epoch_loss = 0for src, tgt in dataloader:tgt_input = tgt[:, :-1]tgt_output = tgt[:, 1:]optimizer.zero_grad()output = model(src, tgt_input)output = output.view(-1, output.shape[-1])tgt_output = tgt_output.reshape(-1)loss = criterion(output, tgt_output)loss.backward()torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)optimizer.step()epoch_loss += loss.item()print(f'Epoch {epoch + 1}, Loss: {epoch_loss / len(dataloader)}')def evaluate(model, dataloader, criterion):model.eval()total_loss = 0with torch.no_grad():for src, tgt in dataloader:tgt_input = tgt[:, :-1]tgt_output = tgt[:, 1:]output = model(src, tgt_input)output = output.view(-1, output.shape[-1])tgt_output = tgt_output.reshape(-1)loss = criterion(output, tgt_output)total_loss += loss.item()print(f'Evaluation Loss: {total_loss / len(dataloader)}')
  • train函数用于训练模型,逐批处理数据,计算损失,并更新模型参数。
  • evaluate函数用于评估模型的性能,计算整个数据集的平均损失。

6. 推理函数

def inference(model, src_sentence, src_vocab, tgt_vocab, max_len=2):model.eval()src_indexes = [src_vocab[word] for word in src_sentence.split()]src_tensor = torch.LongTensor(src_indexes).unsqueeze(0).to(next(model.parameters()).device)  # 确保是 LongTensor 类型sos_idx = tgt_vocab["<sos>"]generated_tensor = model.generate(src_tensor, max_len, sos_idx)generated_sentence = ' '.join([list(tgt_vocab.keys())[i] for i in generated_tensor.squeeze().tolist()])return generated_sentence
  • inference函数用于对单个句子进行翻译,生成对应的目标句子。

7. 运行示例

if __name__ == "__main__":# 假设我们有一个简单的词汇表和句子对vocab = {"<pad>": 0,"<sos>": 1,"<eos>": 2,"hello": 3,"world": 4,"good": 5,"morning": 6,"night": 7,"how": 8,"are": 9,"you": 10,"today": 11,"friend": 12,"goodbye": 13,"see": 14,"take": 15,"care": 16,"welcome": 17,"back": 18}sentences = ["hello world","good morning","goodbye friend","see you","take care","welcome back",]src_vocab = vocabtgt_vocab = vocabsource_sentences = sentencestarget_sentences = sentences# 创建数据集和数据加载器dataset = TranslationDataset(source_sentences, target_sentences, src_vocab, tgt_vocab)dataloader = DataLoader(dataset, batch_size=2, shuffle=True, collate_fn=collate_fn)# 模型初始化model = TransformerModel(len(src_vocab), len(tgt_vocab))optimizer = optim.Adam(model.parameters(), lr=0.0001)criterion = nn.CrossEntropyLoss(ignore_index=0)# 训练模型train(model, dataloader, optimizer, criterion, num_epochs=20)# 评估模型evaluate(model, dataloader, criterion)# 推理测试test_sentence = "hello"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")test_sentence = "see"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")test_sentence = "welcome"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")

在这个运行示例中,我们首先定义了一个简单的词汇表和句子对,然后创建数据集和数据加载器。接下来,我们初始化Transformer模型,设置优化器和损失函数,训练模型并进行评估。最后,通过推理函数对一些输入句子进行翻译,并输出结果。

完整代码实例

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torch.nn.utils.rnn import pad_sequence
import numpy as np# 定义数据集类,用于加载源语言和目标语言的句子
class TranslationDataset(Dataset):def __init__(self, source_sentences, target_sentences, src_vocab, tgt_vocab):self.source_sentences = source_sentences  # 源语言句子列表self.target_sentences = target_sentences  # 目标语言句子列表self.src_vocab = src_vocab  # 源语言词汇表self.tgt_vocab = tgt_vocab  # 目标语言词汇表def __len__(self):return len(self.source_sentences)  # 返回数据集中句子的数量def __getitem__(self, idx):# 将源语言和目标语言的句子转换为词汇表中的索引src = [self.src_vocab[word] for word in self.source_sentences[idx].split()]tgt = [self.tgt_vocab[word] for word in self.target_sentences[idx].split()]return torch.tensor(src), torch.tensor(tgt)  # 返回源句子和目标句子的索引张量# 定义collate_fn函数,用于在批处理中对序列进行填充
def collate_fn(batch):src_batch, tgt_batch = zip(*batch)  # 将批次中的源和目标句子分开src_batch = pad_sequence(src_batch, padding_value=0, batch_first=True)  # 对源句子进行填充tgt_batch = pad_sequence(tgt_batch, padding_value=0, batch_first=True)  # 对目标句子进行填充return src_batch, tgt_batch  # 返回填充后的源和目标句子张量# 定义Transformer模型
class TransformerModel(nn.Module):def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6,dim_feedforward=2048, dropout=0.1):super(TransformerModel, self).__init__()# 定义源语言和目标语言的嵌入层self.embedding_src = nn.Embedding(src_vocab_size, d_model)self.embedding_tgt = nn.Embedding(tgt_vocab_size, d_model)# 定义Transformer模型self.transformer = nn.Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers, dim_feedforward,dropout)# 定义输出的全连接层,将Transformer的输出转换为词汇表中的分布self.fc_out = nn.Linear(d_model, tgt_vocab_size)self.d_model = d_model  # d_model是嵌入向量的维度def forward(self, src, tgt):# 将源语言和目标语言的索引转换为嵌入向量,并进行缩放src = self.embedding_src(src) * np.sqrt(self.d_model)tgt = self.embedding_tgt(tgt) * np.sqrt(self.d_model)# 调整维度以适应Transformer输入的要求src = src.permute(1, 0, 2)tgt = tgt.permute(1, 0, 2)# 将源语言和目标语言嵌入输入到Transformer中output = self.transformer(src, tgt)# 使用全连接层将Transformer的输出转换为目标词汇表中的分布output = self.fc_out(output)return outputdef generate(self, src, max_len, sos_idx):self.eval()  # 设置模型为评估模式# 对源语言进行嵌入并缩放src = self.embedding_src(src) * np.sqrt(self.d_model)src = src.permute(1, 0, 2)  # 调整维度memory = self.transformer.encoder(src)  # 通过编码器获取源语言的记忆表示# 初始化解码器输入,使用<start of sequence>标记ys = torch.ones(1, 1).fill_(sos_idx).type(torch.long).to(src.device)for i in range(max_len - 1):# 对目标语言进行嵌入并缩放tgt = self.embedding_tgt(ys) * np.sqrt(self.d_model)# 生成用于掩码的下三角矩阵,以确保模型不能看到未来的词tgt_mask = nn.Transformer.generate_square_subsequent_mask(tgt.size(0)).to(src.device)# 使用Transformer解码器生成输出out = self.transformer.decoder(tgt, memory, tgt_mask=tgt_mask)out = self.fc_out(out)  # 通过全连接层生成词汇表的分布prob = out[-1, :, :].max(dim=-1)[1]  # 选择概率最大的词作为输出# 将生成的词拼接到解码器的输入中ys = torch.cat([ys, prob.unsqueeze(0)], dim=0)if prob == 2:  # 如果生成了<end of sequence>标记,则停止生成breakreturn ys.transpose(0, 1)  # 返回生成的序列# 训练函数
def train(model, dataloader, optimizer, criterion, num_epochs=10):model.train()  # 设置模型为训练模式for epoch in range(num_epochs):epoch_loss = 0  # 记录每个epoch的损失for src, tgt in dataloader:tgt_input = tgt[:, :-1]  # 获取目标句子中除了最后一个词的部分作为输入tgt_output = tgt[:, 1:]  # 获取目标句子中除了第一个词的部分作为输出optimizer.zero_grad()  # 清零梯度output = model(src, tgt_input)  # 前向传播计算输出output = output.view(-1, output.shape[-1])  # 将输出展平为2D张量tgt_output = tgt_output.reshape(-1)  # 将目标输出展平为1D张量loss = criterion(output, tgt_output)  # 计算损失loss.backward()  # 反向传播计算梯度torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)  # 对梯度进行裁剪以防止梯度爆炸optimizer.step()  # 更新模型参数epoch_loss += loss.item()  # 累加损失print(f'Epoch {epoch + 1}, Loss: {epoch_loss / len(dataloader)}')  # 输出每个epoch的平均损失# 评估函数
def evaluate(model, dataloader, criterion):model.eval()  # 设置模型为评估模式total_loss = 0  # 记录总损失with torch.no_grad():  # 在评估时不需要计算梯度for src, tgt in dataloader:tgt_input = tgt[:, :-1]  # 获取目标句子中除了最后一个词的部分作为输入tgt_output = tgt[:, 1:]  # 获取目标句子中除了第一个词的部分作为输出output = model(src, tgt_input)  # 前向传播计算输出output = output.view(-1, output.shape[-1])  # 将输出展平为2D张量tgt_output = tgt_output.reshape(-1)  # 将目标输出展平为1D张量loss = criterion(output, tgt_output)  # 计算损失total_loss += loss.item()  # 累加损失print(f'Evaluation Loss: {total_loss / len(dataloader)}')  # 输出平均评估损失# 推理函数,用于在模型训练完毕后进行句子翻译
def inference(model, src_sentence, src_vocab, tgt_vocab, max_len=2):model.eval()  # 设置模型为评估模式# 将源语言句子转换为索引序列src_indexes = [src_vocab[word] for word in src_sentence.split()]# 将索引序列转换为张量,并添加批次维度src_tensor = torch.LongTensor(src_indexes).unsqueeze(0).to(next(model.parameters()).device)sos_idx = tgt_vocab["<sos>"]  # 获取<sos>标记的索引# 使用模型生成目标语言的句子generated_tensor = model.generate(src_tensor, max_len, sos_idx)# 将生成的索引序列转换为词语序列generated_sentence = ' '.join([list(tgt_vocab.keys())[i] for i in generated_tensor.squeeze().tolist()])return generated_sentence  # 返回生成的句子# 示例运行
if __name__ == "__main__":# 假设我们有一个简单的词汇表和句子对vocab = {"<pad>": 0,"<sos>": 1,"<eos>": 2,"hello": 3,"world": 4,"good": 5,"morning": 6,"night": 7,"how": 8,"are": 9,"you": 10,"today": 11,"friend": 12,"goodbye": 13,"see": 14,"take": 15,"care": 16,"welcome": 17,"back": 18}sentences = ["hello world","good morning","goodbye friend","see you","take care","welcome back",]src_vocab = vocab  # 源语言词汇表tgt_vocab = vocab  # 目标语言词汇表source_sentences = sentences  # 源语言句子列表target_sentences = sentences  # 目标语言句子列表# 创建数据集和数据加载器dataset = TranslationDataset(source_sentences, target_sentences, src_vocab, tgt_vocab)dataloader = DataLoader(dataset, batch_size=2, shuffle=True, collate_fn=collate_fn)# 模型初始化model = TransformerModel(len(src_vocab), len(tgt_vocab))optimizer = optim.Adam(model.parameters(), lr=0.0001)criterion = nn.CrossEntropyLoss(ignore_index=0)  # 使用交叉熵损失函数,忽略填充标记的损失# 训练模型train(model, dataloader, optimizer, criterion, num_epochs=20)# 评估模型evaluate(model, dataloader, criterion)# 推理测试test_sentence = "hello"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")test_sentence = "see"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")test_sentence = "welcome"translated_sentence = inference(model, test_sentence, src_vocab, tgt_vocab)print(f"Input: {test_sentence}")print(f"Output: {translated_sentence}")

运行结果:

......
Epoch 18, Loss: 0.0005644524741607407
Epoch 19, Loss: 0.0005254073378940424
Epoch 20, Loss: 0.0004640306190898021
Evaluation Loss: 0.00014784792438149452
Input: hello
Output: <sos> world
Input: see
Output: <sos> you
Input: welcome
Output: <sos> back

总结

通过这个教程,我们从理论到实践,详细讲解了Transformer模型的基本原理,并展示了如何使用PyTorch实现一个简单的机器推理模型。虽然这个示例中的模型和数据集都非常简化,但它为进一步学习和研究更复杂的NLP任务打下了基础。希望通过这个教程,你能够对Transformer模型有更深入的理解,并能够在自己的项目中灵活应用。

相关文章:

PyTorch 基础学习(10)- Transformer

系列文章&#xff1a; 《PyTorch 基础学习》文章索引 介绍 Transformer模型是近年来在自然语言处理&#xff08;NLP&#xff09;领域中非常流行的一种模型架构&#xff0c;尤其是在机器翻译任务中表现出了优异的性能。与传统的循环神经网络&#xff08;RNN&#xff09;不同&a…...

mybatis-plus使用

目录 1. 快速开始 1. 创建user表 2. 插入几条数据 3. 创建一个新的springboot项目 4. 导入mybatis-plus依赖 5. 在配置文件中进行配置 6. 编写实体类 7. 编写Mapper 接口类 8. 添加 MapperScan 注解 9. 测试 ​编辑2. CRUD 1. 插入一条语句 2. 根据主键id删除一条记录 3. 根据…...

ant-design-vue快速上手指南及排坑攻略

前言 ant-design-vue是Ant Design的Vue实现&#xff0c;旨在为Vue用户提供一套企业级的UI设计语言。本文将带你快速上手ant-design-vue&#xff0c;并在实践中分享一些常见的坑及解决方法。遵循本文档&#xff0c;让你轻松搭建优雅的Vue应用。 一、环境准备 在开始之前&…...

【GitLab】使用 Docker 安装 3:gitlab-ce:17.3.0-ce.0 配置

参考阿里云的教程docker的重启 sudo systemctl daemon-reload sudo systemctl restart docker配置 –publish 8443:443 --publish 8084:80 --publish 22:22 sudo docker ps -a 當容器狀態為healthy時,說明GitLab容器已經正常啟動。 root@k8s-master-pfsrv:~...

多线程(4)——单例模式、阻塞队列、线程池、定时器

1. 多线程案例 1.1 单例模式 单例模式能保证某个类在程序中只存在唯一一份实例&#xff0c;不会创建出多个实例&#xff08;这一点在很多场景上都需要&#xff0c;比如 JDBC 中的 DataSource 实例就只需要一个 tip&#xff1a;设计模式就是编写代码过程中的 “软性约束”&am…...

告别电量焦虑,高性能65W PD快充芯片HUSB380A打造梦中情【头】

市面上的充电器越来越卷&#xff0c;让人眼花缭乱。压力同样也给到了快充芯片行业&#xff0c;要在激烈的市场竞争中脱颖而出&#xff0c;快充芯片必须集高功率、高性价比与广泛的兼容性等于一身。 基于此&#xff0c;慧能泰推出了新一代高性能PD Source产品——HUSB380A。 图…...

vulnhub靶场 — NARAK

下载地址:https://download.vulnhub.com/ha/narak.ova Description:Narak is the Hindu equivalent of Hell. You are in the pit with the Lord of Hell himself. Can you use your hacking skills to get out of the Narak? Burning walls and demons are around every cor…...

RabbitMQ如何保证消息不丢失

RabbitMQ消息丢失的三种情况 第一种&#xff1a;生产者弄丢了数据。生产者将数据发送到 RabbitMQ 的时候&#xff0c;可能数据就在半路给搞丢了&#xff0c;因为网络问题啥的&#xff0c;都有可能。 第二种&#xff1a;RabbitMQ 弄丢了数据。MQ还没有持久化自己挂了。 第三种…...

(亲测有效)SpringBoot项目集成腾讯云COS对象存储(1)

目录 一、腾讯云对象存储使用 1、创建Bucket 2、使用web控制台上传和浏览文件 3、创建API秘钥 二、代码对接腾讯云COS&#xff08;以Java为例&#xff09; 1、初始化客户端 2、填写配置文件 3、通用能力类 文件上传 测试 一、腾讯云对象存储使用 1、创建Bucket &am…...

无人机之故障排除篇

一、识别故障 掌握基本的无人机系统知识&#xff0c;遵循“先易后难、先外后内、先软件后硬件”的原则进行故障识别。一旦发现故障&#xff0c;立即停止飞行&#xff0c;避免进一步损坏。 二、机械部件维修 对于机身裂痕、螺旋桨损坏等情况&#xff0c;根据损坏程度更换相应部…...

深入理解Python常见数据类型处理

目录 概述数字类型 整数&#xff08;int&#xff09;浮点数&#xff08;float&#xff09;复数&#xff08;complex&#xff09; 字符串&#xff08;str&#xff09; 字符串基本操作字符串方法 列表&#xff08;list&#xff09; 列表基本操作列表方法列表推导式 元组&#xf…...

最佳实践:CI/CD交付模式下的运维展望丨IDCF

李洪锋 启迪万众数字技术(广州)有限公司 &#xff0c;产品研发中心-系统运维部、研发效能&#xff08;DevOps&#xff09;工程师&#xff08;中级&#xff09;课程学员 一、DevOps现状 据云计算产业联盟《中国DevOps现状调查报告2023》显示&#xff0c;国内DevOps 落地成熟度…...

Flat Ads:开发者如何应对全球手游市场的洗牌与转型

2023年下半年至2024年上半年,中国手游的海外市场表现经历了显著变化,开发者要如何应对全球手游市场的洗牌与转型?本篇文章我们将结合相关行业白皮书的最新数据对中国手游出海表现进行分析与洞察。 一、中国手游海外市场表现 根据Sensor Tower《2024年海外手游市场洞察》最新…...

ai取名软件上哪找?一文揭秘5大ai取名生成器

在这个世界上&#xff0c;每一个新生命的到来都是一份奇迹&#xff0c;无论是一个新生儿的第一声啼哭&#xff0c;还是一只宠物的第一次摇尾巴&#xff0c;都充满了无限的希望和喜悦。 然而&#xff0c;给这个小生命起一个响亮、独特且富有意义的名字&#xff0c;往往让人煞费…...

ppt转换成pdf文件,这5个方法一键搞定!小白也能上手~

不管是工作上还是学习上&#xff0c;我们都会遇到转换文档格式的问题。比如常见的pdf转word&#xff0c;ppt转pdf&#xff0c;图片转pdf等。 很多软件都有自带的转换功能可以实现&#xff0c;但是需要保证转换后不乱码&#xff0c;且清晰度足够的方法还是少见的。本文整理了几个…...

中国每个软件创业者都是这个时代的“黑悟空”

作者 | 白鲸开源CEO 郭炜 我作为一个具有30游龄而20年都不碰游戏的游戏玩家&#xff0c;最近为了《黑神话:悟空》&#xff08;简称&#xff0c;黑悟空&#xff09;&#xff0c;不但花重金更新了显卡&#xff0c;还第一次下载了Steam并绑定了支付&#xff0c;为的就是支持这个第…...

解决Qt多线程中fromRawData函数生成的QByteArray数据不一致问题

解决Qt多线程中fromRawData函数生成的QByteArray数据不一致问题 目录 &#x1f514; 问题背景&#x1f4c4; 问题代码❓ 问题描述&#x1fa7a; 问题分析✔ 解决方案 &#x1f514; 问题背景 在开发一个使用Qt框架的多线程应用程序时&#xff0c;我们遇到了一个棘手的问题&…...

datax关于postsql数据增量迁移的问题

看官方文档是不支持的 数据源及同步方案_大数据开发治理平台 DataWorks(DataWorks)-阿里云帮助中心 (aliyun.com) 看了下源码有个postsqlwriter 看了下也就拼接sql 将 PostgresqlWriter中的不允许更新先注释了 让他过去先 然后看到 WriterUtil中的对应方法 getWriteTemplat…...

【Go】实现字符切片零拷贝开销转为字符串

package mainimport ("fmt""unsafe" )func main() {bytes : []byte("hello world")s : *(*string)(unsafe.Pointer(&bytes))fmt.Println(s)bytes[0] Hfmt.Println(s) }slice的底层结构是底层数组、len字段、cap字段。string的底层结构是底层…...

[sqlserver][sql]sqlserver查询执行过的历史sql

SQL是一个针对SQL Server数据库的查询执行过的历史 select * from (SELECT *FROM sys.dm_exec_query_stats QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) ST ) a where a.creation_time >2018-07-18 17:00:00 and charindex(delete from ckcdlist ,text)>0 an…...

python中n次方怎么表示

Python中的n次方用pow()方法来表示&#xff0c;pow()方法返回 xy&#xff08;x的y次方&#xff09;的值。 语法 以下是 math 模块 pow() 方法的语法&#xff1a; import math math.pow( x, y ) 内置的 pow() 方法 pow(x, y[, z]) 函数是计算x的y次方&#xff0c;如果z在存在&…...

Java数组怎么转List,Stream的基本方法使用教程

Stream流 Java 的 Stream 流操作是一种简洁而强大的处理集合数据的方式,允许对数据进行高效的操作,如过滤、映射、排序和聚合。Stream API 于 Java 8 引入,极大地简化了对集合(如 List、Set)等数据的处理。 一、创建 Stream 从集合创建: List<String> list = Ar…...

2024-07-12 - 基于 sealos 部署高可用 K8S 管理系统

摘要 Sealos 是一款以 Kubernetes 为内核的云操作系统发行版。它以云原生的方式&#xff0c;抛弃了传统的云计算架构&#xff0c;转向以 Kubernetes 为云内核的新架构&#xff0c;使企业能够像使用个人电脑一样简单地使用云。 操作实践 1、服务器规划 kubernetes集群大体上…...

Ps:首选项 - 单位与标尺

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“单位与标尺” Units & Rulers选项卡允许用户根据工作需求定制 Photoshop 的测量单位和标尺显示方式。这对于保持工作的一致性和精确性&#xff0c;尤其是在跨设备或跨平台…...

DiskDigger(文件恢复工具) v2.0.3 中文授权版

DiskDigger中文版是一款实用文件恢复工具&#xff0c;它能从任何媒介中恢复误删除的文件。支持硬盘、USB 闪存盘、闪存卡(SD/CF/MMS)、CD、DVD 和软盘等。支持恢复任何格式的文件。DiskDigger 能彻底的扫描每个扇区来跟踪文件&#xff0c;从而最大限度恢复挽回文件信息。 软件…...

C/C++逆向:x96dbg(x64dbg/x86dbg)的使用

这篇文章主要来说一下x96dbg(x64/x86)的基本使用&#xff0c;这里还是使用上篇文章中的简单程序用来作为本篇文章的实例&#xff0c;因为上篇文章再生成程序时选用的解决方案平台为x86所以生成的程序则需要我们使用x32dbg来进行分析。 这边与IDA一样&#xff0c;我们可以将程序…...

超声波清洗机是智商税吗?专业博主分享四大必买超声波清洗机款式

有些人觉得超声波清洗机不过是个“智商税”&#xff0c;花几百块买个小盒子不值当&#xff0c;毕竟自己用手也能清洗。但这种看法过于片面。事实上&#xff0c;超声波清洗已经有几十年的历史&#xff0c;随着科技的发展&#xff0c;现代超声波清洗机不仅能够批量、自动清洁&…...

TIM输出比较

文章目录 前言一、TIM输出比较简介二、PWM简介2.1 PWM基本概念2.2 PWM参数2.3 PWM波形2.4 PWM基本结构2.5 PWM参数计算 三、舵机简介3.1 舵机的工作原理3.2 输入PWM信号的要求3.3 硬件电路 四、直流电机及驱动简介4.1 直流电机4.2 TB6612电机驱动芯片 前言 提示&#xff1a;本…...

JNPF 5.0升级钜惠,感恩回馈永远在路上

尊敬的JNPF用户们&#xff1a; 经过引迈团队数月的辛勤努力和不断的技术创新&#xff0c;JNPF快速开发平台迎来全新升级——5.0版本&#xff01;此次5.0版本的迭代革新&#xff0c;不仅代表着我们技术实力的进一步提升&#xff0c;是我们对用户需求的深度理解和积极回应。为了…...

三维平面电磁铁、交流电磁铁、显微镜磁场北京大学方案

根据用户北京大学需求设计制造方案如下 三维平面电磁铁产品规格 5MPS63-25型三维平面电磁铁&#xff0c;X、Y方向磁场由2对正交的磁极产生&#xff0c;Z轴由一组同轴线圈产生&#xff1b; 每轴对应的两个线圈正接产生均匀磁场&#xff0c;反接产生梯度磁场&#xff1b; …...

做视频网站资质/百度软件应用市场

2019独角兽企业重金招聘Python工程师标准>>> 原理&#xff1a; 在一个矩形上面使用2D纹理贴图 从而显示图片 GL只识别bitmap 所以 jpeg png等格式要解码为bitmap 才能直接生成纹理->渲染 下面是Lite2D Text 渲染freetype生成的bitmap 字体的实现 void Text::draw…...

万宁建设局网站/什么是淘宝搜索关键词

一、课程定位具有高职特色的实用计算机英语网络立体化课程针对国家教育部提出的“重点针对应用型人才”的培养要求&#xff0c;在充分借鉴了当今国外和国内ESP教学的研究成果并结合本教学团队十余年的高职计算机英语教学实践&#xff0c;以培养高职计算机专业学生基于岗位的实际…...

手机可以做网站吗?/百度竞价平台官网

1. 把array转换成list Arrays.asList(arrayName) 2. reverse list Collections.reverse(listName); 3. 找到array里某个元素的位置 int index Arrays.asList(list).indexOf(e); 转载于:https://www.cnblogs.com/warmland/p/5573090.html...

网络网站建设/seo网站推广经理

1&#xff1a;单一职责原则&#xff08;SRP&#xff09;&#xff1a;一个优良的系统设计&#xff0c;强调模块间保持低耦合、高内聚的关系&#xff0c;在面向对象设计中这条规则同样适用&#xff0c;所以面向对象的第一个设计原则就是&#xff1a;单一职责原则&#xff08;SRP&…...

公司做网站走什么费/淘大象排名查询

最近在搞Redis所以装了个virtualbox的ubuntu的虚拟机, redis不是在ubuntu上. 因为需要使用本机客户端访问redis服务&#xff0c;所以需要配置虚拟机和本地机器的双向访问&#xff0c;所以就用到了桥接的上网方式。 废话不多说具体的配置如下&#xff1a; 配置虚拟机IP地址  g…...

导航网站建设/百度seo排名优化

第四章 数组与方法若想要存放一连串相关的数据&#xff0c;使用数组是个相当好用的选择。此外&#xff0c;如果某个程序片段经常反复出现&#xff0c;那么将它定义成一个方法可以有效地简化程序代码。数组是由一组相同类型的变量所组成的数据类型&#xff0c;它们以一个共同的名…...