【Pytorch项目实战】之机器翻译:编码器-解码器、注意力机制AM

news2024/10/8 18:27:06

文章目录

  • 机器翻译 - 中英文翻译
    • 算法一:编码器-解码器(Encoder - Decoder)
    • 算法二:注意力机制(Attention Model,AM)
      • 2.1为何要引入注意力机制?
      • 2.2注意力机制的语义向量表示C计算
      • 2.3每个输入单词的概率分布值计算
    • 算法三:自注意力机制(Self-Attention Model,AM)
    • (一)实战:基于注意力机制的中英文翻译(数据集:MNIST)

机器翻译 - 中英文翻译

(1)传统的统计机器翻译(Statistical Machine Translation,SMT)。如:Encoder-Decoder模型。
(2)神经网络机器翻译(Neural Machine Translation,NMT)。如:带有注意力机制的Encoder-Decoder模型。

算法一:编码器-解码器(Encoder - Decoder)

Encoder - Decoder又被称为Seq2Seq模型。如:CNN/RNN/LSTM/Transformer
直观理解: 由一个句子(或篇章)生成另外一个句子(或篇章)的通用处理模型。对于句子对<Source,Target>,我们的目标是给定输入句子Source,期待通过Encoder-Decoder框架来生成目标句子Target。其中,Source和Target可以是同一种语言,也可以是两种不同的语言。
在这里插入图片描述

算法二:注意力机制(Attention Model,AM)

2.1为何要引入注意力机制?

(1)在翻译“杰瑞”中文单词时,Encoder-Decoder模型中每个英文单词在翻译目标单词“杰瑞”的贡献相同(即中间语义表示C相同),这显然是不合理的,因为“Jerry”对于翻译“杰瑞”更重要。
(2)而引入AM模型,会在翻译“杰瑞”时,体现出Jerry的重要性(即中间语义表示C随当前输出单词变化而变化)。如:[Tom, Chase, Jerry] = [0.3, 0.2, 0.5]。

2.2注意力机制的语义向量表示C计算

在这里插入图片描述
在这里插入图片描述

2.3每个输入单词的概率分布值计算

在<Key,Value>中(英文句子 - 中文句子),给定Target(目标句子)中的某个元素Query(单词)。
(1)计算Query与每个Key的相似性得到**权重系数向量(网络训练),进而得到Target的权重系数矩阵(W是可学习权重参数矩阵);
(2)通过
激活函数softmax将元素Query对应的系数向量变成0~1的概率分布值;
(3)对Value进行
加权求和**,得到Attention值。
在这里插入图片描述

算法三:自注意力机制(Self-Attention Model,AM)

自注意力机制的原理

(一)实战:基于注意力机制的中英文翻译(数据集:MNIST)

数据链接:https://pan.baidu.com/s/1U1t690L0g0Om_ZXXFXC_lw?pwd=o6p4
提取码:o6p4


中文分词库:jieba(安装+介绍+实例)

在这里插入图片描述
在这里插入图片描述

# 主要流程
# (1)数据读取:读取txt文件、数据过滤、中英文分词
# (2)构建模型:编码器、解码器、带注意力机制的解码器
# (3)开始训练:迭代训练函数、训练函数、数据格式转换、计算时长、画图
# (4)评估模型:评估模型并可视化注意力、随机语句评估、评估模型、可视化注意力
########################################################################################################################
from __future__ import unicode_literals, print_function, division

import time
import math
import random
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

from io import open
import unicodedata
import re
import jieba
import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'         # "OMP: Error #15: Initializing libiomp5md.dll"

# import matplotlib.font_manager as fm              # 用于存储和操作字体的属性
# myfont = fm.FontProperties(fname='simhei.ttf')    # 自定义字体
########################################################################################################################
"""(1)读取数据并预处理"""
########################################################
def prepareData(txt_path, lang1, lang2, reverse=False):
    input_lang, output_lang, pairs = readLangs(txt_path, lang1, lang2, reverse)             # 读取txt文本数据
    print("In the txt file, there are %s sentence pairs (lines)" % len(pairs))              # 打印:中英文句子的总数(原文件)
    pairs = filterPairs(pairs)                                                              # 数据过滤
    print("After data processing, there are %s sentence pairs (lines)" % len(pairs))        # 打印:中英文句子的总数(过滤后)
    # 遍历中英文句子(过滤后),并分别进行分词
    for pair in pairs:
        input_lang.addSentence_cn(pair[0])      # 中文分词:使用jieba库
        output_lang.addSentence(pair[1])        # 英文分词:使用空格
    # 打印:中文名+分词后的中文字词数
    # 打印:英文名+分词后的英文单词数
    print("Counted words: (1)%s - %s (2)%s - %s" % (input_lang.name, input_lang.n_words, output_lang.name, output_lang.n_words))
    return input_lang, output_lang, pairs


def readLangs(txt_path, lang1, lang2, reverse=False):
    """txt文本路径,英文名,中文名,位置变换"""
    print("Reading txt file and processing...")
    # 打开指定txt文件,按行读取数据。(txt样本:前部分为英文,后部分为中文,中间用Tab分割)
    lines = open(txt_path % (lang1, lang2), encoding='utf-8').read().strip().split('\n')
    # 遍历所有行,然后分别提取英文与中文,最后进行字符串标准化处理。得到列表结构:pairs[英文, 中文]
    pairs = [[normalizeString(s) for s in l.split('\t')] for l in lines]
    # 判断是否需要转换语句对的次序。如:[英文,中文]转换为[中文,英文]
    if reverse:
        # 数据存放位置变换:pairs[中文, 英文]
        pairs = [list(reversed(p)) for p in pairs]
        input_lang = Lang(lang2)        # 输入中文
        output_lang = Lang(lang1)       # 输出英文(翻译后)
    else:
        input_lang = Lang(lang1)
        output_lang = Lang(lang2)
    return input_lang, output_lang, pairs


def normalizeString(s):
    """字符串标准化处理:英文全部转换为小写、去掉空格及非字母符号"""
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    return s
def unicodeToAscii(s):
    """Unicode字符串转换为ASCII编码"""
    return ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')


class Lang:
    """中英文分词"""
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: "SOS", 1: "EOS"}
        self.n_words = 2  # Count SOS and EOS

    def addSentence(self, sentence):
        """英文分词:使用空格"""
        for word in sentence.split(' '):
            self.addWord(word)

    def addSentence_cn(self, sentence):
        """中文分词:使用jieba库"""
        for word in list(jieba.cut(sentence)):
            self.addWord(word)

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words += 1
        else:
            self.word2count[word] += 1


def filterPairs(pairs):
    """遍历所有中英文句子,并进行数据过滤"""
    return [pair for pair in pairs if filterPair(pair)]
def filterPair(pair):
    """数据过滤"""
    max_length = 20         # 中英文句子最大长度限制
    # prefix词根:通俗上讲就是语文的偏旁部首,词根在单词开头,词缀在结尾。
    english_prefixes = ("i am ", "i m ", "he is", "he s ", "she is", "she s ", "you are", "you re ", "we are", "we re ", "they are", "they re ")
    # 英文开头固定:english_prefixes。且分别判断中文/英文的句子长度是否大于20,是则丢弃。
    return len(pair[0].split(' ')) < max_length and len(pair[1].split(' ')) < max_length and pair[1].startswith(english_prefixes)


########################################################################################################################
"""(2)搭建网络模型"""
########################################################
class EncoderRNN(nn.Module):
    """编码器"""
    def __init__(self, input_size, hidden_size):
        """所有训练句子的(中文)分词数,训练尺度"""
        super(EncoderRNN, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(input_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size)

    def forward(self, input, hidden):                           # 前向传播
        output = self.embedding(input).view(1, 1, -1)           # 随机初始化词向量,词向量值在正态分布(0,1)中随机取值
        output, hidden = self.gru(output, hidden)               # GRU循环网络
        return output, hidden

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)


class DecoderRNN(nn.Module):
    """解码器"""
    def __init__(self, hidden_size, output_size):
        super(DecoderRNN, self).__init__()
        self.hidden_size = hidden_size
        self.embedding = nn.Embedding(output_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size)
        self.out = nn.Linear(hidden_size, output_size)
        self.softmax = nn.LogSoftmax(dim=1)

    def forward(self, input, hidden):                       # 前向传播
        output = self.embedding(input).view(1, 1, -1)       # 随机初始化词向量,词向量值在正态分布(0,1)中随机取值
        output = F.relu(output)                             # 激活函数ReLU
        output, hidden = self.gru(output, hidden)           # 循环网络GRU
        output = self.softmax(self.out(output[0]))          # 分类器
        return output, hidden

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)


class AttnDecoderRNN(nn.Module):
    """带注意力机制的解码器"""
    def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=20):
        """训练尺寸,所有训练句子的(英文)分次数,丢弃率,句子最大长度"""
        super(AttnDecoderRNN, self).__init__()
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.dropout_p = dropout_p
        self.max_length = max_length

        self.embedding = nn.Embedding(self.output_size, self.hidden_size)
        self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
        self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
        self.dropout = nn.Dropout(self.dropout_p)
        self.gru = nn.GRU(self.hidden_size, self.hidden_size)
        self.out = nn.Linear(self.hidden_size, self.output_size)

    def forward(self, input, hidden, encoder_outputs):
        embedded = self.embedding(input).view(1, 1, -1)     # 随机初始化词向量,词向量值在正态分布(0,1)中随机取值
        embedded = self.dropout(embedded)                   # Dropout丢弃率:随机杀死神经元
        attn_weights = F.softmax(self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)      # 分类器(全连接层1)
        attn_applied = torch.bmm(attn_weights.unsqueeze(0), encoder_outputs.unsqueeze(0))       # 计算两个tensor的矩阵乘法
        output = torch.cat((embedded[0], attn_applied[0]), 1)       # 将两个tensor拼接到一起
        output = self.attn_combine(output).unsqueeze(0)             # 全连接层2
        output = F.relu(output)                                     # 激活函数ReLU
        output, hidden = self.gru(output, hidden)                   # 循环网络GRU
        output = F.log_softmax(self.out(output[0]), dim=1)          # 逻辑回归分类器(全连接层3)
        return output, hidden, attn_weights

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)


########################################################################################################################
"""(3)开始训练"""
########################################################
def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, learning_rate=0.01):
    """迭代训练"""
    start = time.time()
    plot_losses = []
    print_loss_total = 0
    plot_loss_total = 0
    encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)       # 优化器SGD(编码器)
    decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)       # 优化器SGD(解码器)
    training_pairs = [tensorsFromPair(random.choice(pairs)) for i in range(n_iters)]
    criterion = nn.NLLLoss()        # NLL损失函数

    for iter in range(1, n_iters + 1):
        training_pair = training_pairs[iter - 1]
        input_tensor = training_pair[0]     # 输入
        target_tensor = training_pair[1]    # 输出
        # 调用训练函数
        loss = train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion)
        print_loss_total += loss
        plot_loss_total += loss

        if iter % print_every == 0:     # 每隔print_every个批次,打印loss值
            print_loss_avg = print_loss_total / print_every
            print_loss_total = 0
            print('Time: %s;    Iter: %d;   Completed: %d %%;   avg_loss: %.4f'
                  % (timeSince(start, iter / n_iters), iter, iter / n_iters * 100, print_loss_avg))

        if iter % plot_every == 0:      # 每隔plot_every个批次,绘制loss图
            plot_loss_avg = plot_loss_total / plot_every
            plot_losses.append(plot_loss_avg)
            plot_loss_total = 0

    showPlot(plot_losses)       # 绘制loss图


def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=20):
    """训练函数"""
    teacher_forcing_ratio = 0.5
    encoder_hidden = encoder.initHidden()       # 参数初始化
    encoder_optimizer.zero_grad()               # 梯度清零(编码器)
    decoder_optimizer.zero_grad()               # 梯度清零(解码器)
    input_length = input_tensor.size(0)         # 获取输入句子长度
    target_length = target_tensor.size(0)       # 获取目标句子长度
    encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

    for ei in range(input_length):
        encoder_output, encoder_hidden = encoder(input_tensor[ei], encoder_hidden)      # Encoder编码器
        encoder_outputs[ei] = encoder_output[0, 0]
    decoder_input = torch.tensor([[SOS_token]], device=device)
    decoder_hidden = encoder_hidden
    use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False

    loss = 0
    if use_teacher_forcing:
        # Teacher forcing: Feed the target as the next input
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(
                decoder_input, decoder_hidden, encoder_outputs)
            loss += criterion(decoder_output, target_tensor[di])
            decoder_input = target_tensor[di]  # Teacher forcing
    else:
        # Without teacher forcing: use its own predictions as the next input
        for di in range(target_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(
                decoder_input, decoder_hidden, encoder_outputs)
            topv, topi = decoder_output.topk(1)
            decoder_input = topi.squeeze().detach()  # detach from history as input

            loss += criterion(decoder_output, target_tensor[di])
            if decoder_input.item() == EOS_token:
                break
    loss.backward()
    encoder_optimizer.step()
    decoder_optimizer.step()
    return loss.item() / target_length


SOS_token = 0       # 中文词典
EOS_token = 1       # 英文词典
def indexesFromSentence(lang, sentence):
    """将【英文句子】拆分为词,并获取每个词的索引号(0:"SOS", 1:"EOS")"""
    return [lang.word2index[word] for word in sentence.split(' ')]
def indexesFromSentence_cn(lang, sentence):
    """将【中文句子】拆分为词,并获取每个词的索引号(0:"SOS", 1:"EOS")"""
    return [lang.word2index[word] for word in list(jieba.cut(sentence))]
def tensorFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    indexes.append(EOS_token)
    return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)
def tensorFromSentence_cn(lang, sentence):
    indexes = indexesFromSentence_cn(lang, sentence)
    indexes.append(EOS_token)
    return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)
def tensorsFromPair(pair):
    input_tensor = tensorFromSentence_cn(input_lang, pair[0])       # 输入语句转化为Tensor
    target_tensor = tensorFromSentence(output_lang, pair[1])        # 输出语句转化为Tensor
    return (input_tensor, target_tensor)


def timeSince(since, percent):
    """未训练前的时间,当前批次在所有训练批次中的占比"""
    now = time.time()       # 获取当前时间
    s = now - since         # 计算从开始到当前批次数据的训练总耗时
    es = s / (percent)      # 当前批次总耗时/占比 = 项目总耗时
    rs = es - s
    return '%s / %s' % (asMinutes(s), asMinutes(es))
def asMinutes(s):
    """计算训练所耗时间(分/秒)"""
    m = math.floor(s / 60)      # 计算分钟:向上取整
    s -= m * 60                 # 计算秒
    return '%dm %ds' % (m, s)


def showPlot(points):
    """画图"""
    fig = plt.figure()                  # 新建画板
    ax = fig.add_subplot(111)           # 添加Axes
    # this locator puts ticks at regular intervals
    loc = ticker.MultipleLocator(base=0.2)
    ax.yaxis.set_major_locator(loc)
    plt.plot(points)


########################################################################################################################
"""(4)评估模型"""
########################################################
def evaluateRandomly(encoder, decoder, n=10):
    """随机语句评估"""
    for i in range(n):
        pair = random.choice(pairs)     # 随机采样
        print('>', pair[0])             # 输入
        print('=', pair[1])             # 目标
        output_words, attentions = evaluate(encoder, decoder, pair[0])
        output_sentence = ' '.join(output_words)
        print('<', output_sentence)     # 输出
        print('')


def evaluateAndShowAttention(input_sentence):
    """评估模型并可视化注意力"""
    output_words, attentions = evaluate(encoder1, attn_decoder1, input_sentence)        # 评估模型
    showAttention(input_sentence, output_words, attentions)                             # 可视化模型
    print('input =', input_sentence)                # 打印输入语句
    print('output =', ' '.join(output_words))       # 打印输出语句


def evaluate(encoder, decoder, sentence, max_length=20):
    """评估模型"""
    with torch.no_grad():       # 强制之后的内容不及逆行计算图构建
        input_tensor = tensorFromSentence_cn(input_lang, sentence)
        input_length = input_tensor.size()[0]
        encoder_hidden = encoder.initHidden()
        encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)

        for ei in range(input_length):
            encoder_output, encoder_hidden = encoder(input_tensor[ei], encoder_hidden)      # Encoder编码器
            encoder_outputs[ei] += encoder_output[0, 0]
        decoder_input = torch.tensor([[SOS_token]], device=device)  # SOS
        decoder_hidden = encoder_hidden

        decoded_words = []
        decoder_attentions = torch.zeros(max_length, max_length)
        for di in range(max_length):
            decoder_output, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_hidden, encoder_outputs)
            decoder_attentions[di] = decoder_attention.data
            topv, topi = decoder_output.data.topk(1)
            if topi.item() == EOS_token:
                decoded_words.append('<EOS>')
                break
            else:
                decoded_words.append(output_lang.index2word[topi.item()])
            decoder_input = topi.squeeze().detach()
        return decoded_words, decoder_attentions[:di + 1]


def showAttention(input_sentence, output_words, attentions):
    """可视化注意力"""
    fig = plt.figure()              # 新建画板
    ax = fig.add_subplot(111)       # 添加Axes
    cax = ax.matshow(attentions.numpy(), cmap='bone')       # 在窗口显示数组矩阵
    fig.colorbar(cax)                                       # 显示colorbar

    ax.set_xticklabels([''] + list(jieba.cut(input_sentence)) + ['<EOS>'], rotation=90, fontproperties=None)
    ax.set_yticklabels([''] + output_words)
    ###########################################################
    # MultipleLocator(n):将刻度间隔设置为n的倍数
    # FormaStrFormatter('%1.1f'):设置主刻度标签文本的格式
    #
    # ax.xaxis.set_major_locator(MultipleLocator(n)):设置主刻度标签的位置
    # ax.xaxis.set_major_formatter(FormaStrFormatter('%1.1f')):设置主刻度标签文本的格式
    ###########################################################
    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))       # 设置主刻度标签的位置
    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))       # 设置主刻度标签的位置
    plt.show()


########################################################################################################################
if __name__ == '__main__':
    ###########################################################
    # (51)读取数据、数据预处理
    txt_path = r'E:\Pytorch\eng-cmn\%s-%s.txt'
    input_lang, output_lang, pairs = prepareData(txt_path, lang1='English', lang2='Chinese', reverse=True)
    print(random.choice(pairs))     # 随机打印一组中英文句子
    ###########################################################
    # (52)开始训练
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    encoder1 = EncoderRNN(input_size=input_lang.n_words, hidden_size=256).to(device)                        # 编码器Encoder
    attn_decoder1 = AttnDecoderRNN(hidden_size=256, output_size=output_lang.n_words,
                                   dropout_p=0.1, max_length=20).to(device)                                 # 带注意力机制的解码器
    trainIters(encoder=encoder1, decoder=attn_decoder1, n_iters=25000, print_every=500, plot_every=100)    # 迭代训练
    ###########################################################
    # (53)随机语句评估
    evaluateRandomly(encoder1, attn_decoder1, n=5)
    ###########################################################
    # (54)评估模型并可视化注意力
    evaluateAndShowAttention("我很幸福。")
    evaluateAndShowAttention("我们在严肃地谈论你的未来。")
    evaluateAndShowAttention("我在家。")
    evaluateAndShowAttention("我们在严肃地谈论你的未来。")
    

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

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

相关文章

2023年首更,警惕6本SCISSCI期刊被剔除

2023年1月17日&#xff0c;Web of Science核心期刊目录首次更新。此次SCIE & SSCI期刊目录更新&#xff0c;与上次更新&#xff08;2022年12月&#xff09;相比&#xff0c;共有6本期刊被剔除出SCIE & SSCI期刊目录&#xff0c;详情如下&#xff1a; 公众号“Unionpub…

Redis实战9-全局唯一ID

发布优惠券的时候&#xff0c;每个店铺都可以发布优惠券&#xff0c;当用户抢购的时候,优惠券表中的id如果使用数据库的自增长ID会存在以下问题&#xff1a; 1&#xff1a;id的规律太明显&#xff0c;容易被刷 2&#xff1a;当数据量很大的时候&#xff0c;会受到单表数据的限…

三、分布式id,lua脚本,分布式锁,消息队列

文章目录优惠卷秒杀1.redis实现分布式ID2.优惠券秒杀下单3.超卖问题4.lua脚本5.分布式锁6.redis stream消息队列实现异步秒杀7.redis消息队列list实现消息队列PubSub实现消息队列stream实现消息队列stream的消息队列-消费者组学习黑马点评项目整理总结:https://www.bilibili.co…

5 个用于自动化的杀手级 Python 脚本

Python 是一种功能强大的语言&#xff0c;广泛用于自动执行各种任务。无论您是开发人员、系统管理员&#xff0c;还是只是想通过自动化日常任务来节省时间的人&#xff0c;Python 都能满足您的需求。 这里有 5 个 Python 脚本&#xff0c;可以帮助您自动执行各种任务 文章目录…

Spring Boot之Swagger

文章目录零 项目源码一 Swagger简介二 Spring Boot集成Swagger2.1 环境准备2.2 配置Swagger2.3 配置文档信息2.4 配置扫描接口2.5 配置开关Swagger2.6 配置API分组2.7 配置实体类2.8 常用注解零 项目源码 Swagger源码免费下载 一 Swagger简介 Swagger官网swagger是一款可以根…

深入开源和金融技术世界,《新程序员005》来了!

亲爱的 CSDN 以及《新程序员》的读者朋友们&#xff0c;《新程序员005&#xff1a;开源深度指南 & 新金融背后的科技力量》正式与大家见面&#xff01;现在&#xff0c;点击下方封面&#xff0c;即可订阅&#xff0c;立享电子书&#xff0c;纸质书将在春节后为大家寄出&…

9、循环语句

目录 一、while循环语句 二、do…while循环语句 三、for循环语句 1. for语句 2. foreach语句 一、while循环语句 while语句也称条件判断语句&#xff0c;它的循环方式为利用一个条件来控制是否要继续反复执行这个语句。 语法如下&#xff1a; 当条件表达式的返回值为真时…

【并发编程十三】c++原子操作(1)

【并发编程十三】c原子操作&#xff08;1&#xff09;一、改动序列1、改动序列2、预测执行二、原子操作及其类别1、原子操作2、非原子操作3、原子类型三、标准原子类型1、标准原子类型的两种实现方式2、原子操作的用途3、原子操作的宏四、操作std:atomic_flag1、简介2、使用说明…

jsp ssm宿舍报修换宿管理系统-宿管idea

目 录 1 1绪论 1 1.1 课题研究的背景 1 1.2 课题研究的意义 1 1.3 本文主要工作 1 2 关键技术介绍 3 2.1 JSP技术的简介 3 2.2 SSM框架 3 2.3 MYSQL数据库 4 2.4 MySQL环境配置 4 2.5 B/S架构 4 3 需求分析与可行性分析 5 3.1功能需求分析…

年后公司缺人,面了13个测试员,发现他们都有一个通病.....

公司缺人&#xff0c;面了不少测试&#xff0c;结果竟然没有一个合适的。 一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。 看简历很多都是3年工作经验&#xff0c;但面试中…

数据不幸丢失怎么办?恢复丢失数据的 7 大方法

有时&#xff0c;您的系统可能会无意中被数据丢失等不可预见的事件唤醒。数据丢失已成为我们数字经济中的普遍现象&#xff0c;其发生的方式多种多样&#xff0c;包括意外删除、病毒攻击、无法访问的分区、数据损坏&#xff0c;甚至更灾难性的情况&#xff0c;如数据盗窃。 不…

Spring进阶:3步引入MybatisPlus多数据源,详细配置及原理解析

前言 MybatisPlus(MP)作为mybatis的增强工具&#xff0c;提供了配置多数据源的扩展&#xff0c;通过简单的几步配置&#xff0c;即可使用注解轻松切换数据源。 以下是dynamic-datasource提供的功能列表&#xff1a; 使用方法 1&#xff0c;引入dynamic-datasource-spring-bo…

第六章 数组、排序和查找

一、数组&#xff08;P156&#xff09; 1. 数组介绍 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。即&#xff1a;数(数据)组(一组)就是一组数据2. 数组的使用 2.1 使用方式1&#xff1a;动态初始化 数组的定义&#xff1a;数据类型 …

uniCloud 微信小程序登陆全流程demo

ps&#xff1a; 博主一向不喜欢废话&#xff0c;直接说几个点&#xff0c;你悟了就直接去试&#xff0c;可以不用看完&#xff0c;还是得自己去试印象才深刻&#xff0c;博主写博文就怕自己以后忘记做个笔记顺便能帮一个是一个 1、你要获取unionid 肯定得访问外网&#xff0c;u…

【C++初阶】六、STL---string(总)|为什么学习string类|标准库中的string类|string 类常用接口

目录 一、为什么学习string类 1.1 C语言中的字符串 1.2 字符串在OJ面试中 二、标准库中的string类 2.1 string 介绍 2.2 编码格式 三、string 类常用接口 3.1 Member functions&#xff08;成员函数&#xff09; 3.1.1 构造函数&#xff08;Construct&#xff09; 3.…

C++11 并发指南七(C++11 内存模型一:介绍)

C11 并发指南七(C11 内存模型一&#xff1a;介绍) 文章目录C11 并发指南七(C11 内存模型一&#xff1a;介绍)第六章主要介绍了 C11 中的原子类型及其相关的API&#xff0c;原子类型的大多数 API 都需要程序员提供一个 std::memory_order&#xff08;可译为内存序&#xff0c;访…

面试第一次被问到SDK测试,当时就懵了

01、是什么 客户端SDK是为第三方开发者提供的软件开发工具包&#xff0c;包括SDK接口、开发文档和Demo示例等。SDK和应用之间是什么关系呢&#xff1f;以云信即时消息服务为例&#xff0c;如下图所示&#xff0c;应用客户端通过调用云信SDK接口&#xff0c;进行消息等数据查询…

[前端笔记——CSS] 14.图像、媒体和表单元素

[前端笔记——CSS] 14.图像、媒体和表单元素1.CSS调整大小2.图片、媒体和表单元素2.1替换元素2.2 form元素2.3 举个实例1.CSS调整大小 一个空的<div>是没有尺寸的。如果在 HTML 文件中添加一个空<div> 并给予其边框&#xff0c;则会在页面上看到一条线。 <div…

【软件安装】ubuntu安装nvidia驱动,解决闪屏问题

Official Drivers | NVIDIA nvidia驱动下载官网&#xff0c;登陆速度是有点慢的&#xff0c;需要耐心等待 我的电脑是笔记本&#xff0c;ubuntu18.04.6&#xff0c;NVIDIA GeForce RTX 3050 Laptop GPU 天时地利人和的情况下&#xff0c;按照下面这篇博文就可以顺利安装nvidi…

【存储】etcd的存储是如何实现的(2)

在上一篇中&#xff0c;介绍了etcd底层存储的内容&#xff0c;包括wal、raft.MemoryStorage以及backend。在介绍backend时提到了backend只是etcd kv存储的一部分&#xff0c;负责持久化存储&#xff0c;backend加内存化treeIndex才构成etcd完整的支持mvcc的kv存储。所以这篇就来…