语言模型(language model)
文章目录
- 引言
- 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. 语言模型的分类
- 基于统计语言模型
- 对于一份语料进行词频、词序、词共现的统计
- 计算相关概率得到语言模型
- 代表:N-gram语言模型
- 基于神经网络的语言模型
- 根据设定好的网络结构使用语料进行模型训练
- 代表:LSTM语言模型,BERT等
- 自回归(auto regressive)语言模型
- 在训练时由上文预测下文(或反过来)
- 单向模型,仅使用单侧序列信息
- 代表:N-gram,ELMO
- 自编码(auto encoding)语言模型
- 在训练时预测序列中任意位置的字符
- 双向模型,吸收上下文信息
- 代表:BERT
4. N-gram语言模型
N-gram语言模型是一种基础的语言模型,用于预测下一个词或字符出现的概率,基于前N-1个词或字符。该模型将文本看作是词或字符的有序序列,并假设第n个词仅与前N-1个词相关。
比如,在一个bigram(2-gram)模型中,每个词的出现只依赖于它前面的一个词。例如,"我吃"之后是"苹果"的概率可以表示为P(苹果|我吃)。
优点:
- 计算简单:只需要统计词频和条件词频。
- 实现容易:不需要复杂的算法。
缺点:
- 稀疏性问题:随着N的增加,模型需要的存储空间急剧增加,而大多数N-gram组合在实际数据中可能并不存在。
- 上下文限制:只能捕捉到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(错|气不)
马尔科夫假设的缺陷:
- 影响第n个词的因素可能出现在前面很远的地方
long distance dependency例:我读过关于马尔科夫的生平的书
我看过关于马尔科夫的生平的电影
我听过关于马尔科夫的生平的故事
- 影响第n个词的因素可能出现在其后面
- 影响第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语言模型-平滑方法
- 回退(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 defaultdictclass NgramLanguageModel:def __init__(self, corpus=None, n=3):self.n = nself.sep = "_" # 用来分割两个词,没有实际含义,只要是字典里不存在的符号都可以self.sos = "<sos>" #start of sentence,句子开始的标识符self.eos = "<eos>" #end of sentence,句子结束的标识符self.unk_prob = 1e-5 #给unk分配一个比较小的概率值,避免集外词概率为0self.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()#将文本切分成词或字或tokendef 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 cngram_prefix = self.sep.join(ngram_splits[:-1]) #ngram_prefix :a bngram_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_countself.ngram_count_prob_dict[window_size][ngram] = count / ngram_prefix_countreturn#获取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_probelse:#高于一阶的可以回退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 = 0for 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:utf8import 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 tokenvocab["\n"] = 1return 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_sizewindow = 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 = 0model.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) #计算losswatch_loss.append(loss.item())loss.backward() #计算梯度optim.step() #更新权重print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))if not save_weight:returnelse: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 语言模型的应用-话者分离
- 根据说话内容判断说话人
- 常用于语言识别系统中,判断录音对话中角色
- 如客服对话录音,判断坐席或客户
- 根据不同腔调判断说话人
- 翻译腔: 这倒霉的房子里竟然有蟑螂,你可以想象到吗?这真是太可怕了!
- 港台腔:你这个人怎么可以这个样子
- 东北味: 我不稀得说你那些事儿就拉倒了
- 本质上为文本分类任务
- 对于每个类别,使用类别语料训练语言模型
- 对于一个新输入的文本,用所有语言模型计算成句概率
- 选取概率最高的类别为预测类别

- 相比一般文本分类模型,如贝叶斯,rf,神经网络等
- 优势:
- 每个类别模型互相独立,样本不均衡或样本有错误对其他模型没有影响
- 可以随时增加新的类别,而不影响旧的类别的效果
- 效果上讲:一般不会有显著优势
- 效率上讲:一般会低于统一的分类模型
6.2 语言模型的应用-文本纠错
- 纠正文本中的错误
- 如:
- 我今天去了天暗门看人民英雄记念碑
- 我今天去了天安门看人民英雄纪念碑
- 错误可能是同音字或形近字等
- 对每一个字建立一个混淆字集合
- 计算整句话成句概率
- 用混淆字集合中的词替代原句中的字,重新计算概率
- 选取得分最高的一个候选句子,如果这个句子比原句的得分增长超过一定的阈值
- 对下一个字重复步骤3-4,直到句子末尾

这种方式有一些缺陷:
- 无法解决多字少字问题
- 阈值的设置非常难把握,如果设置过大,达不到纠错效果;如果设置过小,造成大量替换,有可能改变句子的原意
- 混淆字字表难以完备
- 语言模型的领域性会影响修改结果
- 连续的错字会大幅提升纠错难度
- 一般工业做法:
- 限定一个修改白名单,只判断特定的字词是否要修改
- 如限定只对所有发音为shang wu的片段,计算是否修改为“商务”,其余一概不做处理
- 对于深度学习模型而言,错别字是可以容忍的,所以纠错本身的重要性在下降,一般只针对展示类任务
6.3 语言模型的应用-数字归一化
- 将一个文本中的数字部分转化成对读者友好的样式
- 常见于语言识别系统后,展示文本时使用
- 如:
- 秦皇岛港煤炭库存量在十一月初突然激增,从四百五十四点九万吨增加到七百七十三点四万吨,打破了一九九九年以来的记录
- 秦皇岛港煤炭库存量在11月初突然激增,从454.9万吨增加到773.4万吨,打破了1999年以来的记录
- 找到数字形式符合规范的文本作为原始语料
- 用正则表达式找到数字部分(任意形式)
- 将数字部分依照其格式替换为<阿拉伯数字><汉字数字><汉字连读>等token
- 使用带token文本训练语言模型
- 对于新输入的文本,同样使用正则表达式找到数字部分,之后分别带入各个token,使用语言模型计算概率
- 选取概率最高的token最为最终数字格式,按照规则转化后填入原文本

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


- 本质为序列标注任务
- 可以依照类似方式,处理分词、文本加标点、文本段落切分等任务
- 分词或切分段落只需要一种token;打标点时,可以用多种分隔token,代表不同标点
7. 总结
- 语言模型的核心能力是计算成句概率,依赖这一能力,可以完成大量不同类型的NLP任务。
- 基于统计的语言模型和基于神经网络的语言模型各有使用的场景,大体上讲,基于统计的模型优势在于解码速度,而神经网络的模型通常效果更好。
- 单纯通过PPL评价语言模型是有局限的,通过下游任务效果进行整体评价更好。
- 深入的理解一种算法,有助于发现更多的应用方式。
- 看似简单(甚至错误)的假设,也能带来有意义的结果,事实上,这是简化问题的常见方式。
相关文章:
语言模型(language model)
文章目录 引言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. 语言…...
【3.Vue子组件调用父组件方法】
1.概述 使用组件建抛出事件的方式来调用父组件的方法,不直接用this.$parent.function的方法,当然这种方式是可以的。 2.代码实现 2.1 父组件代码 父组件写一个方法给子组件调用 // 设备点击事件// equipId:设备id// leftValue:left值// topValue:top…...
算法系列-876-求链表的中间节点
求链表中间节点,如果有两个中间节点取后面那个 链表定义 // lc codestart /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(…...
h5 ws 客户端 监听ws服务器广播的信息
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>AI智能写作</title><!-- Bootstrap CSS --><meta charset"utf-8"><meta name"viewport" content"widt…...
网络基础之重中之重
目录 IP协议 编辑 基本概念: 协议头格式: 编辑 网段划分 DHCP : CIDR: 特殊的IP地址: IP地址的数量限制: 私有IP和公网IP 路由 路由的过程: 数据链路层 认识以太网&#x…...
HarmonyOS应用开发者-----基础认证试题及答案
HarmonyOS应用开发者基础认证试题及答案 试题会不定时刷新,本试题仅供大家学习参考 【判断题】 2.5/2.5 所有使用@Component修饰的自定义组件都支持onPageShow,onBackPress和onPageHide生命周期函数。 正确(True)错误(False) 回答正确【判断题】 2.5/2.5 在Column和Row容器组…...
C++:string并非以0作为结束符,c_str和data的返回却包含结束符0
C语言中使用char数组保存字符串时,是以字符为0或者\0作为字符串的结束符标志的。 所以一个char str[10]的数组只能合法的保存9个字符(因为最后还要加一个结束符)。 #include <cstring> #include <iostream>using namespace std;int main() {char str[10] ="…...
ChatGPT插件的优缺点
虽然西弗吉尼亚大学的研究人员看到了最新的官方ChatGPT插件——名为“代码解释器”( Code Interpreter)的教育应用潜力,但他们也发现,对于使用计算方法处理针对癌症和遗传疾病的定向治疗的生物数据的科学家来说,这款插…...
北京985学校,交叉学科考英一数三408
北京师范大学(B) 考研难度(☆☆☆) 内容:23考情概况(拟录取和复试分析)、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1096字,预计阅读:3分钟 2023考情概况 北…...
ChatGPT 总结前端HTML, JS, Echarts都包含哪些内容
AIGC ChatGPT ,BI商业智能, 可视化Tableau, PowerBI, FineReport, 数据库Mysql Oracle, Office, Python ,ETL Excel 2021 实操,函数,图表,大屏可视化 案例实战 http://t.csdn.cn/zBytu...
企业架构LNMP学习笔记1
项目开发流程: 公司老板或者产品经理,根据市场调查,决定开发一整套互联网产品。 互动社交电商用户论坛(BBS) 产品决策 (老板产品UI设计) 业务实施、代码开发 程序开发人员 前端开发&#x…...
【位运算】leetcode371:两整数之和
一.题目描述 两整数之和 二.思路分析 题目要求我们实现两整数相加,但是不能使用加号,应该立马想到是用位运算来解决问题。之前说过,异或就是“无进位相加”,故本题可以先将两数异或,然后想办法让得到的结果进位即可。…...
【爬虫小知识】如何利用爬虫爬网页——python爬虫
前言 网络时代的到来,给我们提供了海量的信息资源,但是,想要获取这些信息,手动一个一个网页进行查找,无疑是一项繁琐且效率低下的工作。这时,爬虫技术的出现,为我们提供了一种高效的方式去获取…...
什么是跨域问题 ?Spring MVC 如何解决跨域问题 ?Spring Boot 如何解决跨域问题 ?
目录 1. 什么是跨域问题 ? 2. Spring MVC 如何解决跨域问题 ? 3. Spring Boot 如何解决跨域问题 ? 1. 什么是跨域问题 ? 跨域问题指的是不同站点之间,使用 ajax 无法相互调用的问题。 跨域问题的 3 种情况&#x…...
线性代数的学习和整理17:向量空间的基,自然基,基变换等(未完成)
目录 3 向量空间的基:矩阵的基础/轴 3.1 从颜色RGB说起 3.2 附属知识 3.3 什么样的向量可以做基? 3.4 基的分类 3.1.1 不同空间的基---向量组的数量可能不同 3.1.2 自然基 3.1.3 正交基 3.1.4 标准正交基 3.1.5 基和向量/矩阵 3.1.6 基变换 …...
Java中支持分库分表的框架/组件/中间件简介
文章目录 1 sharding-jdbc2 TSharding3 Atlas4 Cobar5 MyCAT6 TDDL7 Vitess 列举一些比较常见的,简单介绍一下: sharding-jdbc(当当) TSharding(蘑菇街) Atlas(奇虎360) Cobar&#…...
7.2 项目2 学生通讯录管理:文本文件增删改查(C 版本)(自顶向下设计+断点调试) (A)
C自学精简教程 目录(必读) 该作业是 作业 学生通讯录管理:文本文件增删改查(C版本) 的C 语言版本。 具体的作业题目描述,要求,可以参考 学生通讯录管理:文本文件增删改查(C版本)。…...
excel怎么设置任意选一个单元格纵横竖横都有颜色
有时excel表格内容过多的时候,我们通过excel设置任意选一个单元格纵横,竖横背景颜色,这样会更加具有辨识度。设置方式截图如下 设置成功后,预览的效果图...
期货-股票交易规则
交易时间 港股:9:00~9:20 集合竞价,9:3012:00,13:0016:00 持续交易,16:00~16:10 随机收市竞价沪股:9:00~9:25 集合竞价,9:3011:30,13:0015:00 持续交易,11:30~12:00 交易申报深股&a…...
Makefile一些语法
ifneq($(filter true,$(xxx)), )的含义 filter 是过滤的意思,它的原型是:$(filter PATTERN…,TEXT), 意义为:过滤掉字串“TEXT”中所有不符合模式“PATTERN”的单词,保留所有符合此模式的单词做返回值。 结合前面的if…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...
