语言模型(language model)

news2024/11/23 21:15:57

文章目录

  • 引言
  • 1. 什么是语言模型
  • 2. 语言模型的主要用途
    • 2.1 言模型-语音识别
    • 2.2 语言模型-手写识别
    • 2.3 语言模型-输入法
  • 3. 语言模型的分类
  • 4. N-gram语言模型
    • 4.1 N-gram语言模型-平滑方法
    • 4.2 ngram代码
    • 4.3 语言模型的评价指标
    • 4.4 两类语言模型的对比
  • 5. 神经网络语言模型
  • 6. 语言模型的应用
    • 6.1 语言模型的应用-话者分离
    • 6.2 语言模型的应用-文本纠错
    • 6.3 语言模型的应用-数字归一化
    • 6.4 语言模型的应用-文本打标
  • 7. 总结

引言

语言模型是一种用于预测文本序列中下一个词或字符的概率分布的模型。它可以捕获语言结构的某些方面,如语法、句式和上下文信息。传统的语言模型通常使用N-gram方法或隐藏马尔可夫模型,但这些模型往往不能捕捉到长距离依赖和复杂的语义信息。

1. 什么是语言模型

通俗来讲
语言模型评价一句话是否“合理”或“是人话”
数学上讲
P(今天天气不错) > P(今错不天天气)
语言模型用于计算文本的成句概率

2. 语言模型的主要用途

2.1 言模型-语音识别

  • 语音识别:声音 -> 文本
  • 声音本质是一种波
    在这里插入图片描述
  • 将波按时间段切分很多帧,如25ms一段
  • 之后进行声学特征提取,将每一帧转化成一个向量
  • 以声学特征提取后的向量为输入,经过声学模型,预测得到音素
    在这里插入图片描述
  • 音素与拼音类似,但要考虑声调
  • 音素序列对应多条文本序列,由语言模型挑选出成句概率最高的序列
  • 使用beam search或维特比的方式解码
  • 语音识别示意图
    在这里插入图片描述

2.2 语言模型-手写识别

在这里插入图片描述

  • 识别模型将图片中文字转化为候选汉字(一般分定位和识别两步),再有语言模型挑选出成句概率最高的序列

2.3 语言模型-输入法

  • 输入即为拼音序列,每个拼音自然的有多个候选汉字,根据语言模型挑选高概率序列
  • 输入法是一个细节繁多的任务,在语言模型这一基础算法上,需要考虑常见的打字手误,常见误读,拼音缩略,中英混杂,输出符号,用户习惯等能力
  • 手写输入法,语音输入法同理

3. 语言模型的分类

  1. 基于统计语言模型
  • 对于一份语料进行词频、词序、词共现的统计
  • 计算相关概率得到语言模型
  • 代表:N-gram语言模型
  1. 基于神经网络的语言模型
  • 根据设定好的网络结构使用语料进行模型训练
  • 代表:LSTM语言模型,BERT等
  1. 自回归(auto regressive)语言模型
  • 在训练时由上文预测下文(或反过来)
  • 单向模型,仅使用单侧序列信息
  • 代表:N-gram,ELMO
  1. 自编码(auto encoding)语言模型
  • 在训练时预测序列中任意位置的字符
  • 双向模型,吸收上下文信息
  • 代表:BERT

4. N-gram语言模型

N-gram语言模型是一种基础的语言模型,用于预测下一个词或字符出现的概率,基于前N-1个词或字符。该模型将文本看作是词或字符的有序序列,并假设第n个词仅与前N-1个词相关。

比如,在一个bigram(2-gram)模型中,每个词的出现只依赖于它前面的一个词。例如,"我吃"之后是"苹果"的概率可以表示为P(苹果|我吃)。

优点:

  1. 计算简单:只需要统计词频和条件词频。
  2. 实现容易:不需要复杂的算法。

缺点:

  1. 稀疏性问题:随着N的增加,模型需要的存储空间急剧增加,而大多数N-gram组合在实际数据中可能并不存在。
  2. 上下文限制:只能捕捉到N-1个词的上下文信息。

尽管有这些局限性,N-gram模型由于其简单和高效仍然在很多应用场景中被广泛使用,如拼写检查、语音识别和机器翻译等。

如何计算成句概率?

  • 用S代表句子,w代表单个字或词
  • S = w1w2w3w4w5…wn
  • P(S) = P(w1,w2,w3,w4,w5…wn)
  • 成句概率 -> 词W1~Wn按顺序出现的概率
  • P(w1,w2,w3,…,wn) = P(w1)P(w2|w1)P(w3|w1,w2)…P(wn|w1,…,wn-1)

以字为单位

  • P(今天天气不错) = P(今)*P(天|今) *P(天|今天) *P(气|今天天) *P(不|今天天气) *P(错|今天天气不)

以词为单位

  • P(今天 天气 不错) = P(今天)*P(天气|今天) *P(不错|今天 天气)

如何计算P(今天)?

  • P(今天) = Count(今天) / Count_total 语料总词数
  • P(天气|今天) = Count(今天 天气) / Count(今天)
  • P(不错|今天 天气) = Count(今天 天气 不错) / Count(今天 天气)
  • 二元组:今天 天气 2 gram
  • 三元组:今天 天气 不错 3 gram

困难:句子太多了!

  • 对任意一门语言,N-gram数量都非常庞大,无法穷举,需要简化

马尔科夫假设

  • P(wn|w1,…,wn-1) ≈ P(wn|wn-3,wn-2,wn-1)
  • 假设第n个词出现的概率,仅受其前面有限个词影响
  • P(今天天气不错) = P(今)*P(天|今) *P(天|今天) *P(气|天天) *P(不|天气) *P(错|气不)

马尔科夫假设的缺陷:

  1. 影响第n个词的因素可能出现在前面很远的地方
    long distance dependency

例:我读过关于马尔科夫的生平的书
我看过关于马尔科夫的生平的电影
我听过关于马尔科夫的生平的故事

  1. 影响第n个词的因素可能出现在其后面
  2. 影响第n个词的因素可能并不在文中

但是,马尔科夫假设下依然可以得到非常有效的模型

语料:
今天 天气 不错 P(今天) = 3 / 12 = 1/4
明天 天气 不错 P(天气|今天) = 2 / 3
今天 天气 不行 P(不错|今天 天气) = 1 / 2
今天 是 晴天 P(不错|天气) = 2/3
3 gram模型
P(今天 天气 不错) = P(今天)*P(天气|今天) *P(不错|今天 天气) = 1 / 12
2 gram模型
P(今天 天气 不错) = P(今天)*P(天气|今天) *P(不错|天气) = 1 / 9

语料:
今天 天气 不错 P(今天) = 3 / 12 = 1/4
明天 天气 不错 P(天气|今天) = 2 / 3
今天 天气 不行 P(不错|今天 天气) = 1 / 2
今天 是 晴天 P(不错|天气) = 2/3

问题:如何给出语料中没出现过的词或ngram概率?
P(今天 天气 糟糕) = P(今天)*P(天气|今天) *P(糟糕|天气)

  • 平滑问题(smoothing)
  • 理论上说,任意的词组合成的句子,概率都不应当为零
  • 如何给没见过的词或ngram分配概率即为平滑问题
  • 也称折扣问题(discounting)

4.1 N-gram语言模型-平滑方法

  1. 回退(backoff)

当三元组a b c不存在时,退而寻找b c二元组的概率
P(c | a b) = P(c | b) * Bow(ab)
Bow(ab)称为二元组a b的回退概率
回退概率有很多计算方式,甚至可以设定为常数
回退可以迭代进行,如序列 a b c d
P(d | a b c) = P(d | b c) * Bow(abc)
P(d | bc) = P(d | c) * Bow(bc)
P(d | c ) = P(d) * Bow©

P(word)不存在如何处理
加1平滑 add-one smooth
对于1gram概率P(word) = Count(word)+1/Count(total_word)+V
V为词表大小
对于高阶概率同样可以
在这里插入图片描述

将低频词替换为
预测中遇到的未见过的词,也用代替
一语成谶 -> 一语成
P(|一 语 成)
这是一种nlp处理未登录词(OOV)的常见方法

插值
受到回退平滑的启发,在计算高阶ngram概率是同时考虑低阶的ngram概率值,以插值给出最终结果
在这里插入图片描述

实践证明,这种方式效果有提升
λ 可以在验证集上调参确定

4.2 ngram代码

import math
from collections import defaultdict


class NgramLanguageModel:
    def __init__(self, corpus=None, n=3):
        self.n = n
        self.sep = "_"     # 用来分割两个词,没有实际含义,只要是字典里不存在的符号都可以
        self.sos = "<sos>"    #start of sentence,句子开始的标识符
        self.eos = "<eos>"    #end of sentence,句子结束的标识符
        self.unk_prob = 1e-5  #给unk分配一个比较小的概率值,避免集外词概率为0
        self.fix_backoff_prob = 0.4  #使用固定的回退概率
        self.ngram_count_dict = dict((x + 1, defaultdict(int)) for x in range(n))
        self.ngram_count_prob_dict = dict((x + 1, defaultdict(int)) for x in range(n))
        self.ngram_count(corpus)
        self.calc_ngram_prob()

    #将文本切分成词或字或token
    def sentence_segment(self, sentence):
        return sentence.split()
        #return jieba.lcut(sentence)

    #统计ngram的数量
    def ngram_count(self, corpus):
        for sentence in corpus:
            word_lists = self.sentence_segment(sentence)
            word_lists = [self.sos] + word_lists + [self.eos]  #前后补充开始符和结尾符
            for window_size in range(1, self.n + 1):           #按不同窗长扫描文本
                for index, word in enumerate(word_lists):
                    #取到末尾时窗口长度会小于指定的gram,跳过那几个
                    if len(word_lists[index:index + window_size]) != window_size:
                        continue
                    #用分隔符连接word形成一个ngram用于存储
                    ngram = self.sep.join(word_lists[index:index + window_size])
                    self.ngram_count_dict[window_size][ngram] += 1
        #计算总词数,后续用于计算一阶ngram概率
        self.ngram_count_dict[0] = sum(self.ngram_count_dict[1].values())
        return

    #计算ngram概率
    def calc_ngram_prob(self):
        for window_size in range(1, self.n + 1):
            for ngram, count in self.ngram_count_dict[window_size].items():
                if window_size > 1:
                    ngram_splits = ngram.split(self.sep)              #ngram        :a b c
                    ngram_prefix = self.sep.join(ngram_splits[:-1])   #ngram_prefix :a b
                    ngram_prefix_count = self.ngram_count_dict[window_size - 1][ngram_prefix] #Count(a,b)
                else:
                    ngram_prefix_count = self.ngram_count_dict[0]     #count(total word)
                # word = ngram_splits[-1]
                # self.ngram_count_prob_dict[word + "|" + ngram_prefix] = count / ngram_prefix_count
                self.ngram_count_prob_dict[window_size][ngram] = count / ngram_prefix_count
        return

    #获取ngram概率,其中用到了回退平滑,回退概率采取固定值
    def get_ngram_prob(self, ngram):
        n = len(ngram.split(self.sep))
        if ngram in self.ngram_count_prob_dict[n]:
            #尝试直接取出概率
            return self.ngram_count_prob_dict[n][ngram]
        elif n == 1:
            #一阶gram查找不到,说明是集外词,不做回退
            return self.unk_prob
        else:
            #高于一阶的可以回退
            ngram = self.sep.join(ngram.split(self.sep)[1:])
            return self.fix_backoff_prob * self.get_ngram_prob(ngram)


    #回退法预测句子概率
    def calc_sentence_ppl(self, sentence):
        word_list = self.sentence_segment(sentence)
        word_list = [self.sos] + word_list + [self.eos]
        sentence_prob = 0
        for index, word in enumerate(word_list):
            ngram = self.sep.join(word_list[max(0, index - self.n + 1):index + 1])
            prob = self.get_ngram_prob(ngram)
            # print(ngram, prob)
            sentence_prob += math.log(prob)
        return 2 ** (sentence_prob * (-1 / len(word_list)))



if __name__ == "__main__":
    corpus = open("sample.txt", encoding="utf8").readlines()
    lm = NgramLanguageModel(corpus, 3)
    print("词总数:", lm.ngram_count_dict[0])
    print(lm.ngram_count_prob_dict)
    print(lm.calc_sentence_ppl("e f g b d"))

4.3 语言模型的评价指标

  • 困惑度 perplexity
    在这里插入图片描述
  • PPL值与成句概率成反比

一般使用合理的目标文本来计算PPL,若PPL值低,则说明成句概率高,也就说明由此语言模型来判断,该句子的合理性高,这样是一个好的语言模型

  • 另一种PPL,用对数求和代替小数乘积
    在这里插入图片描述
  • 本质是相同的,与成句概率呈反比
  • 思考:PPL越小,语言模型效果越好,这一结论是否正确?
  • 成句概率是个相对值!

4.4 两类语言模型的对比

在这里插入图片描述

5. 神经网络语言模型

  • Bengio et al. 2003
  • 与ngram模型相似使用前n个词预测下一个词
  • 输出在字表上的概率分布
  • 得到了词向量这一副产品

在这里插入图片描述

  • 随着相关研究的发展,隐含层模型结构的复杂度不断提升
  • DNN -> CNN/RNN -> LSTM/GRU -> transformer

在这里插入图片描述

  • Devlin et al. 2018 BERT 诞生
  • 主要特点:不再使用预测下一个字的方式训练语言模型,转而预测文本中被随机遮盖的某个字
  • 这种方式被称为MLM(masked language model)
  • 实际上这种方式被提出的时间非常早,并非bert原创

代码

#coding:utf8

import torch
import torch.nn as nn
import numpy as np
import math
import random
import os
import re
import matplotlib.pyplot as plt

"""
基于pytorch的rnn语言模型
"""

class LanguageModel(nn.Module):
    def __init__(self, input_dim, vocab):
        super(LanguageModel, self).__init__()
        self.embedding = nn.Embedding(len(vocab) + 1, input_dim)
        self.layer = nn.RNN(input_dim, input_dim, num_layers=2, batch_first=True)
        self.classify = nn.Linear(input_dim, len(vocab) + 1)
        self.dropout = nn.Dropout(0.1)
        self.loss = nn.functional.cross_entropy

    #当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.embedding(x)  #output shape:(batch_size, sen_len, input_dim)
        x, _ = self.layer(x)      #output shape:(batch_size, sen_len, input_dim)
        x = x[:, -1, :]        #output shape:(batch_size, input_dim)
        x = self.dropout(x)
        y_pred = self.classify(x)   #output shape:(batch_size, input_dim)
        if y is not None:
            return self.loss(y_pred, y)
        else:
            return torch.softmax(y_pred, dim=-1)

#读取语料获得字符集
#输出一份
def build_vocab_from_corpus(path):
    vocab = set()
    with open(path, encoding="utf8") as f:
        for index, char in enumerate(f.read()):
            vocab.add(char)
    vocab.add("<UNK>") #增加一个unk token用来处理未登录词
    writer = open("vocab.txt", "w", encoding="utf8")
    for char in sorted(vocab):
        writer.write(char + "\n")
    return vocab

#加载字表
def build_vocab(vocab_path):
    vocab = {}
    with open(vocab_path, encoding="utf8") as f:
        for index, line in enumerate(f):
            char = line[:-1]        #去掉结尾换行符
            vocab[char] = index + 1 #留出0位给pad token
        vocab["\n"] = 1
    return vocab

#加载语料
def load_corpus(path):
    return open(path, encoding="utf8").read()

#随机生成一个样本
#从文本中截取随机窗口,前n个字作为输入,最后一个字作为输出
def build_sample(vocab, window_size, corpus):
    start = random.randint(0, len(corpus) - 1 - window_size)
    end = start + window_size
    window = corpus[start:end]
    target = corpus[end]
    # print(window, target)
    x = [vocab.get(word, vocab["<UNK>"]) for word in window]   #将字转换成序号
    y = vocab[target]
    return x, y

#建立数据集
#sample_length 输入需要的样本数量。需要多少生成多少
#vocab 词表
#window_size 样本长度
#corpus 语料字符串
def build_dataset(sample_length, vocab, window_size, corpus):
    dataset_x = []
    dataset_y = []
    for i in range(sample_length):
        x, y = build_sample(vocab, window_size, corpus)
        dataset_x.append(x)
        dataset_y.append(y)
    return torch.LongTensor(dataset_x), torch.LongTensor(dataset_y)

#建立模型
def build_model(vocab, char_dim):
    model = LanguageModel(char_dim, vocab)
    return model


#计算文本ppl
def calc_perplexity(sentence, model, vocab, window_size):
    prob = 0
    model.eval()
    with torch.no_grad():
        for i in range(1, len(sentence)):
            start = max(0, i - window_size)
            window = sentence[start:i]
            x = [vocab.get(char, vocab["<UNK>"]) for char in window]
            x = torch.LongTensor([x])
            target = sentence[i]
            target_index = vocab.get(target, vocab["<UNK>"])
            if torch.cuda.is_available():
                x = x.cuda()
            pred_prob_distribute = model(x)[0]
            target_prob = pred_prob_distribute[target_index]
            prob += math.log(target_prob, 10)
    return 2 ** (prob * ( -1 / len(sentence)))


def train(corpus_path, save_weight=True):
    epoch_num = 10        #训练轮数
    batch_size = 128       #每次训练样本个数
    train_sample = 10000   #每轮训练总共训练的样本总数
    char_dim = 128        #每个字的维度
    window_size = 6       #样本文本长度
    vocab = build_vocab("vocab.txt")       #建立字表
    corpus = load_corpus(corpus_path)     #加载语料
    model = build_model(vocab, char_dim)    #建立模型
    if torch.cuda.is_available():
        model = model.cuda()
    optim = torch.optim.Adam(model.parameters(), lr=0.001)   #建立优化器
    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch in range(int(train_sample / batch_size)):
            x, y = build_dataset(batch_size, vocab, window_size, corpus) #构建一组训练样本
            if torch.cuda.is_available():
                x, y = x.cuda(), y.cuda()
            optim.zero_grad()    #梯度归零
            loss = model(x, y)   #计算loss
            watch_loss.append(loss.item())
            loss.backward()      #计算梯度
            optim.step()         #更新权重
        print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
    if not save_weight:
        return
    else:
        base_name = os.path.basename(corpus_path).replace("txt", "pth")
        model_path = os.path.join("model", base_name)
        torch.save(model.state_dict(), model_path)
        return

#训练corpus文件夹下的所有语料,根据文件名将训练后的模型放到莫得了文件夹
def train_all():
    for path in os.listdir("corpus"):
        corpus_path = os.path.join("corpus", path)
        train(corpus_path)


if __name__ == "__main__":
    # build_vocab_from_corpus("corpus/all.txt")
    # train("corpus.txt", True)
    train_all()

6. 语言模型的应用

6.1 语言模型的应用-话者分离

  • 根据说话内容判断说话人
  • 常用于语言识别系统中,判断录音对话中角色
  • 如客服对话录音,判断坐席或客户
  • 根据不同腔调判断说话人
  • 翻译腔: 这倒霉的房子里竟然有蟑螂,你可以想象到吗?这真是太可怕了!
  • 港台腔:你这个人怎么可以这个样子
  • 东北味: 我不稀得说你那些事儿就拉倒了
  • 本质上为文本分类任务
  1. 对于每个类别,使用类别语料训练语言模型
  2. 对于一个新输入的文本,用所有语言模型计算成句概率
  3. 选取概率最高的类别为预测类别

在这里插入图片描述

  • 相比一般文本分类模型,如贝叶斯,rf,神经网络等
  • 优势:
  • 每个类别模型互相独立,样本不均衡或样本有错误对其他模型没有影响
  • 可以随时增加新的类别,而不影响旧的类别的效果
  • 效果上讲:一般不会有显著优势
  • 效率上讲:一般会低于统一的分类模型

6.2 语言模型的应用-文本纠错

  • 纠正文本中的错误
  • 如:
  • 我今天去了天门看人民英雄念碑
  • 我今天去了天安门看人民英雄纪念碑
  • 错误可能是同音字或形近字等
  1. 对每一个字建立一个混淆字集合
  2. 计算整句话成句概率
  3. 用混淆字集合中的词替代原句中的字,重新计算概率
  4. 选取得分最高的一个候选句子,如果这个句子比原句的得分增长超过一定的阈值
  5. 对下一个字重复步骤3-4,直到句子末尾

在这里插入图片描述

这种方式有一些缺陷:

  1. 无法解决多字少字问题
  2. 阈值的设置非常难把握,如果设置过大,达不到纠错效果;如果设置过小,造成大量替换,有可能改变句子的原意
  3. 混淆字字表难以完备
  4. 语言模型的领域性会影响修改结果
  5. 连续的错字会大幅提升纠错难度
  • 一般工业做法:
  • 限定一个修改白名单,只判断特定的字词是否要修改
  • 如限定只对所有发音为shang wu的片段,计算是否修改为“商务”,其余一概不做处理
  • 对于深度学习模型而言,错别字是可以容忍的,所以纠错本身的重要性在下降,一般只针对展示类任务

6.3 语言模型的应用-数字归一化

  • 将一个文本中的数字部分转化成对读者友好的样式
  • 常见于语言识别系统后,展示文本时使用
  • 如:
  • 秦皇岛港煤炭库存量在十一月初突然激增,从四百五十四点九万吨增加到七百七十三点四万吨,打破了一九九九年以来的记录
  • 秦皇岛港煤炭库存量在11月初突然激增,从454.9万吨增加到773.4万吨,打破了1999年以来的记录
  1. 找到数字形式符合规范的文本作为原始语料
  2. 用正则表达式找到数字部分(任意形式)
  3. 将数字部分依照其格式替换为<阿拉伯数字><汉字数字><汉字连读>等token
  4. 使用带token文本训练语言模型
  5. 对于新输入的文本,同样使用正则表达式找到数字部分,之后分别带入各个token,使用语言模型计算概率
  6. 选取概率最高的token最为最终数字格式,按照规则转化后填入原文本

在这里插入图片描述

6.4 语言模型的应用-文本打标

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

  • 本质为序列标注任务
  • 可以依照类似方式,处理分词、文本加标点、文本段落切分等任务
  • 分词或切分段落只需要一种token;打标点时,可以用多种分隔token,代表不同标点

7. 总结

  1. 语言模型的核心能力是计算成句概率,依赖这一能力,可以完成大量不同类型的NLP任务。
  2. 基于统计的语言模型和基于神经网络的语言模型各有使用的场景,大体上讲,基于统计的模型优势在于解码速度,而神经网络的模型通常效果更好。
  3. 单纯通过PPL评价语言模型是有局限的,通过下游任务效果进行整体评价更好。
  4. 深入的理解一种算法,有助于发现更多的应用方式。
  5. 看似简单(甚至错误)的假设,也能带来有意义的结果,事实上,这是简化问题的常见方式。

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

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

相关文章

开发一款AR导览导航小程序多少钱?ar地图微信小程序 ar导航 源码

随着科技的不断发展&#xff0c;增强现实&#xff08;AR&#xff09;技术在不同领域展现出了巨大的潜力。AR导览小程序作为其中的一种应用形式&#xff0c;为用户提供了全新的观赏和学习体验。然而&#xff0c;开发一款高质量的AR导览小程序需要投入大量的时间、人力和技术资源…

C语言 数字在升序数组中出现的次数

目录 1.题目描述 2.题目分析 2.1遍历数组方法 2.2二分查找方法 2.3代码示例 数字在升序数组中出现的次数 这道题可以用遍历数组和二分查找来处理 1.题目描述 2.题目分析 题目中有一个关键信息&#xff0c;非降序数组&#xff0c;我们可以使用if语句来处理这个问题 if(…

记录一次Modbus通信的置位错误

老套路&#xff0c;一图胜千言&#xff0c;框图可能有点随意&#xff0c;后面我会解释 先描述下背景&#xff0c;在Modbus线程内有一个死循环&#xff0c;一直在读8个线圈的状态&#xff0c;该线程内读到的消息会直接发送给UI线程&#xff0c;UI线程会解析Modbus数据帧&#xf…

Hightopo 使用心得(6)- 3D场景环境配置(天空球,雾化,辉光,景深)

在前一篇文章《Hightopo 使用心得&#xff08;5&#xff09;- 动画的实现》中&#xff0c;我们将一个直升机模型放到了3D场景中。同时&#xff0c;还利用动画实现了让该直升机围绕山体巡逻。在这篇文章中&#xff0c;我们将对上一篇的场景进行一些环境上的丰富与美化。让场景更…

【排序】快速排序——为什么这个排序最快?

以从小到大的顺序进行说明。 定义 快排是Hoare在1962年&#xff08;彼时的中国&#xff0c;是三年困难时期&#xff0c;好好学习建设祖国&#xff01;&#xff09;提出的基于二叉树结构的排序。 为什么说是基于二叉树&#xff1f; 因为这种排序每次选出一个基准值&#xff0c;…

35、下载、安装 jdk11 记录,Idea中把项目从 jdk8 换 jdk 11

之前一直用jdk8&#xff0c;现在改成 11的试试看 登录官网下载这个11 https://www.oracle.com/cn/java/technologies/downloads/#java11-windows 下载jdk的oracle官网 需要自己注册oracle账户 修改环境变量的 JAVA_HOME Path 路径这里原本添加8的时候有了&#xff0c;不…

构建高性能云原生大数据处理平台:融合人工智能优化数据分析流程

文章目录 架构要点优势与应用案例研究&#xff1a;基于云原生大数据平台的智能营销分析未来展望&#xff1a;大数据与人工智能的融合结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏…

【MySQL系列】MySQL复合查询的学习 _ 多表查询 | 自连接 | 子查询 | 合并查询

「前言」文章内容大致是对MySQL复合查询的学习。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、基本查询回顾二、多表查询三、自连接四、子查询4.1 单行子查询4.2 多行子查询4.3 多列子查询4.4 在from子句中使用子查询 五、合并查询 一、基本查询回顾…

RabbitMQ的镜像队列

镜像队列 如果 RabbitMQ 集群中只有一个 Broker 节点&#xff0c;那么该节点的失效将导致整体服务的临时性不可用&#xff0c;并且也可能会导致消息的丢失。可以将所有消息都设置为持久化&#xff0c;并且对应队列的durable 属性也设置为 true &#xff0c;但是这样仍然无法…

ubuntu学习(五)----读取文件以及光标的移动

1、读取文件函数原型介绍 ssize_t read(int fd,void*buf,size_t count) 参数说明&#xff1a; fd: 是文件描述符 buf:为读出数据的缓冲区&#xff1b; count: 为每次读取的字节数&#xff08;是请求读取的字节数&#xff0c;读上来的数据保存在缓冲区buf中&#xff0c;同时文…

jmeter递增压测线程组配置

jmeter递增压测线程组配置 新建线程组线程组参数详解及填写其他指标设置 新建线程组 操作位置如图&#xff1a; 线程组参数详解及填写 其他指标设置 其他指标设置可参考另一篇文章&#xff1a; 链接: jmeter 在linux服务器中执行性能测试、监听服务器资源指标

单例模式的相关知识

饿汉模式 package Thread; class Singleton{private static Singleton instance new Singleton();public static Singleton getInstance(){return instance;}private Singleton(){} }public class demo1 {public static void main(String[] args) {Singleton S1 Singleton.ge…

Sentinel dashboard无法查询到应用的限流配置问题以及解决

一。问题引入 使用sentinle-dashboard控制台 项目整体升级后&#xff0c;发现控制台上无法看到流控规则了 之前的问题是无法注册上来 现在是注册上来了。结果看不到流控规则配置了。 关于注册不上来的问题&#xff0c;可以看另一篇文章 https://blog.csdn.net/a15835774652/…

Mybatis与Spring集成配置

目录 具体操作 1.1.添加依赖 1.2创建spring的配置文件 1.3. 注解式开发 Aop整合pagehelper插件 1. 创建一个AOP切面 2. Around("execution(* *..*xxx.*xxx(..))") 表达式解析 前言&#xff1a; 上篇我们讲解了关于Mybatis的分页&#xff0c;今天我们讲Mybatis与…

如何向BertModel增加字符

这里写自定义目录标题 看起来add_special_tokens和add_tokens加入的新token都不会被切分。

paddleclas ImportError: cannot import name ‘Identity‘ from ‘paddle.nn‘

使用paddlepaddle的 paddleclas 官方demos时 &#xff0c;报错如图 ImportError: cannot import name ‘Identity’ from ‘paddle.nn’ 解决方案很简单&#xff1a; 找到调用 Identity 的位置&#xff1a; 注释掉就解决啦 !!! 搞定&#xff01;&#xff01;&#xff01;…

4.14 tcp_tw_reuse 为什么默认是关闭的?

开启 tcp_tw_reuse 参数可以快速复用处于 TIME_WAIT 状态的 TCP 连接时&#xff0c;相当于缩短了 TIME_WAIT 状态的持续时间。 tcp_tw_reuse 是什么&#xff1f; TIME_WAIT 状态的持续时间是 60 秒&#xff0c;这意味着这 60 秒内&#xff0c;客户端一直会占用着这个端口。端…

使用训练工具

HuggingFace上提供了很多已经训练好的模型库&#xff0c;如果想针对特定数据集优化&#xff0c;那么就需要二次训练模型&#xff0c;并且HuggingFace也提供了训练工具。 一.准备数据集 1.加载编码工具 加载hfl/rbt3编码工具如下所示&#xff1a; def load_encode():# 1.加载编…

用C/C++修改I2C默认的SDA和SCL针脚

首先要说明一点&#xff1a;Pico 有两个 I2C&#xff0c;也就是两套 SDA 和 SCL。这点你可以在针脚图中名字看出&#xff0c;比如下图的 Pin 4 和 Pin 5是 I2C1 的&#xff0c;而默认的 Pin 6 和 Pin 7 是 I2C0 的。 默认情况下是只开启了第一个 I2C&#xff0c;也就是只有 I2C…

数据库——缓存数据

文章目录 缓存数据的处理流程是怎样的&#xff1f;为什么要用 Redis/为什么要用缓存&#xff1f; 缓存数据的处理流程是怎样的&#xff1f; 简单来说就是: 如果用户请求的数据在缓存中就直接返回。缓存中不存在的话就看数据库中是否存在。数据库中存在的话就更新缓存中的数据。…