深度学习 - 53.Bert 简介与 Keras-Bert 常用示例

news2024/11/27 6:35:41

目录

一.引言

二.Bert 简介

1.Embedding Layer

2.Encoder layer

3.Pre-training 与 Fine-Tuning

三.Keras-Bert 常用 Demo

1.获取预训练模型

2.加载预训练模型

3.Fill Text 

4.IsCorrelation

5.Get Embedding

6.完整代码

四. Fine-Tuning

五.Bert VS OpenAI GPT

六.总结


一.引言

Bert 是 Google 在 18 年发布的 <Pre-training of Deep Bidirectional Transformers for Language Understanding> 中提到的预训练模型,其适用于 NLP 场景且可以基于自己的文本数据进行微调,下面简单介绍下其原理以及 Keras-Bert 的一些基本应用。

二.Bert 简介

Bert 主要由一个 Embedding 层和多个 Encoder 层堆叠而成,下面做简单介绍。

1.Embedding Layer

在构建 bert 输入时,会在每个 Text 的最前面增加一个特殊符号 '[CLS]',除此之外,不同 Sentence 之间还会加入 '[SEP]' 分隔符,标识一个句子的结束。

以上图的句子为例:

sentence_1 = "my dog is cute"
sentence_2 = "he likes playing"
print('Tokens:', tokenizer.tokenize(first=sentence_1, second=sentence_2))
indices, segments = tokenizer.encode(first=sentence_1, second=sentence_2)
print('Indices:', indices)
print('Segments:', segments)

Token 可以认为是分词以及增加特殊符号标识后的句子,通过 tokenizer 转换得到其 index。

Segment 代表每个字符属于第几个句子,上面第一个句子对应索引为0,第二个句子为1。

Position 代表每个字符在整个 Text 的位置信息索引。

Bert 实际使用中输入层使用了 Token Embedding、Segment Embedding 与 Position Embedding 三类向量,将每个类型的 Embedding concat 后再将对应位置的 Embedding 相加就得到原始的 Input。这里 Bert 预训练模型的 Embedding 维度为 768。

2.Encoder layer

Bert 在模型结构上主要使用了 Encoder 的堆叠,其中应用了 Multi-Head Attention 与 Scaled Dot-Product,关于 Multi-Head Attention 与 Scaled Dot Product 我们前面 Attention 的文章已经做了介绍。其可以看作是只使用了 Transformer 的 Encoder 结构,在 Input Embedding 的基础上 Add 了 Position Embedding,随后经过一层 Multi-Head Attention 并且将 Input 与 Attention 的结果相加,避免信息丢失,随后经过一层前馈网络,再执行一次 Add & Norm 进入下一个 Encoder。

  

3.Pre-training 与 Fine-Tuning

BERT 的全面预训练模型和微调模型。预训练和微调中除了输出层外其余部分使用了相同的架构。相同的预训练模型参数用于初始化不同下游任务的模型。在微调过程中,对所有参数进行微调。根据任务场景不同,Bert 可以完成不同的任务:

• Sentence Pair Classification Tasks 文本对分类任务

• Single Sentence Classification Tasks 单句分类任务

• Question Answering Task 问答任务

• Single Sentence Tagging Task 单句标记任务

上面不同类型任务图下给出了不同的数据集,可用于 Bert 模型的微调,大家也可以使用自己场景的文本问答、新闻报刊等数据进行训练。

 

三.Keras-Bert 常用 Demo

下述代码运行使用的环境为:

import tensorflow as tf
import platform

print("TF: %s Keras: %s Python: %s" % (tf.__version__, tf.keras.__version__, platform.python_version()))
TF: 2.4.0-rc0 Keras: 2.4.0 Python: 3.8.6

1.获取预训练模型

一般情况下,我们主要使用预训练好的 Bert 进行一些基本的 NLP 任务或者通过 Fine-Tuning 对 Bert 进行一些微调,这里我们加载预训练模型 Bert-Base uncased_L-12_H-768_A-12,其模型名称代表 12-Layer、768-Hidden, 12-Heads 共计约 110M parameters,可以通过 wget 获取该预训练模型:

 wget -q https://storage.googleapis.com/bert_models/2018_10_18/uncased_L-12_H-768_A-12.zip

除了 Bert-Base 外,还有 Bert-Large,其包含 340M 个参数的模型,包含 24-Layer 以及 1024-Hidden 以及 16-Heads。一些基础的 Bert 模型可以到 Bert 的 github 选择下载:

 

2.加载预训练模型

import numpy as np
from keras_bert import load_vocabulary, load_trained_model_from_checkpoint, Tokenizer, get_checkpoint_paths
from keras_bert.datasets import get_pretrained, PretrainedList


class Bert:
    _token_dict = None  # word ->index
    _token_dict_inv = None  # index -> word
    _model = None
    _export_model = None
    _tokenizer = None

    def __init__(self, _model_path):
        self._model_path = model_path
        self.loadBertModel(self._model_path)
        self._tokenizer = Tokenizer(self._token_dict)

    def loadBertModel(self, path):

        paths = get_checkpoint_paths(path)

        # 加载预训练模型
        self._model = load_trained_model_from_checkpoint(paths.config, paths.checkpoint, training=True, seq_len=None)
        self._export_model = load_trained_model_from_checkpoint(paths.config, paths.checkpoint, seq_len=10)
        self._model.summary(line_length=120)

        # 字典映射与反映射
        self._token_dict = load_vocabulary(paths.vocab)
        self._token_dict_inv = {v: k for k, v in self._token_dict.items()}

通过 load_trained_model_from_checkpoint 读取预训练模型,seq_len = None 的模型主要处理不定长的 sentence 问题,seq_len 固定的 export 模型主要用于 Embedding 获取。token_dict 和 token_dict_inv 为 kv 相反的 map,token_dict 形式如下,共包含不同的符号 21128 个:

顺便调用 model.summary() 看下模型结构:

•  Embedding

包含 Token、Segment 与 Position Embedding,最后经过 Dropout 与 Norm 进入后续逻辑。

•  Encoder

将 Embedding 层得到的 Embedding-Norm 进行 MultiHeadSelfAttention,以及 Dropout 以及 Norm 随后接 FeedForward 前馈网络接着继续 Dropout。注意两个红框位置,MultiHeadAttention 与 FeedForward 后得到的向量斗鱼原始输入的向量进行了 Add 操作:

这里 Add 操作可以避免随着 Encoder 堆叠层数的增加,原始信息的不断衰减。

• Output

最后的输出层 MLM (masked) 代表 Masked Language Model 可以进行序列重建,NSP 代表 Next Sentence Prediction 可以进行句子间的二分类任务,例如判断后一句话与前一句话是否有关联。由于加载的是 Bert-Base,所以最后的 Layer 是 Encoder-12,且模型的 Total params 约为 102882442 即 100m。

3.Fill Text 

句子填字可以实现句子补全与填空,让 bert 填入最合适 [概率最大的词]。

    # 填字
    def fillText(self, _text, _st, _end):
        tokens = self._tokenizer.tokenize(_text)

        for i in range(_st, _end + 1):
            tokens[i] = '[MASK]'
        print('Tokens:', tokens)

        # 映射后的索引、代表句子、代表Mask掩码
        indices = np.array([[self._token_dict[token] for token in tokens]])
        segments = np.array([[0] * len(tokens)])

        masks = np.array([[0] * len(tokens)])
        for i in range(_st, _end + 1):
            masks[0][i] = 1

        # 填字预测
        predicts = self._model.predict([indices, segments, masks])[0].argmax(axis=-1).tolist()
        print('Fill with: ', list(map(lambda x: self._token_dict_inv[x], predicts[0][_st:_end + 1])))

text 为对应填词的文本,st 与 end 可以指定 mask 掩码的位置然后让 bert 在这个范围内进行填充,注意由于 bert Input Embedding 时会在开头添加 '[CLS]',所有对应位置的索引需要 +1,所以下面示例中 1、2 对应的是 '数学': 

    # 填充字符
    text = '数学是利用符号语言研究数量、结构、变化以及空间等概念的一门学科'
    st, end = 1, 2
    bert.fillText(text, st, end)
    st, end = 4, 5
    bert.fillText(text, st, end)

 原文的前五个字为 '数学是利用',我们分别将 '数学' 和 '利用' 进行 MASK 处理,这里分别填充了 '数学' 和 '使用',基本匹配:

 

4.IsCorrelation

是否相关判断 sentence_1 和 sentence_2 在语料训练后的文本场景下,前后语句是否有关联。

    # 两个句子是否有关联
    def hasCorrelation(self, _sentence_1, _sentence_2):
        indices, segments = self._tokenizer.encode(first=_sentence_1, second=_sentence_2)
        masks = np.array([[0] * len(indices)])

        predicts = self._model.predict([np.array([indices]), np.array([segments]), masks])[1]
        is_correlation = bool(np.argmax(predicts, axis=-1)[0])
        print('%s && %s is has Correlation: ' % (_sentence_1, _sentence_2), is_correlation)
        return is_correlation

 给定三个学科相关的语句,让 Bert-Base 判断前后语句是否存在关联:

    # 判断前后逻辑是否一致
    sentence_1 = '数学是利用符号语言研究數量、结构、变化以及空间等概念的一門学科。'
    sentence_2 = '从某种角度看屬於形式科學的一種。'
    sentence_3 = '任何一个希尔伯特空间都有一族标准正交基。'
    bert.hasCorrelation(sentence_1, sentence_2)
    bert.hasCorrelation(sentence_1, sentence_3)

sentence_1 与 sentence_2 无关,sentenct_1 与 sentenct_3 相关:

 

5.Get Embedding

上篇介绍多样性时介绍了 MMR 算法,其通过预训练向量计算不同 item 之间的 sim(i,j),这里 bert 就可以作为预训练 Embedding 获取的模型。

• 获取词向量

获取向量时需要用  expert_model,且 encode 的 max_len 参数要与模型的 max_len 参数对应。这里实际获取的向量维度为 768,大家也可以不使用 [:5] 截断。

    def getCharEmbedding(self, _text):
        tokens = self._tokenizer.tokenize(_text)
        print('Tokens:', tokens)
        indices, segments = self._tokenizer.encode(first=_text, max_len=10)
        predicts = self._export_model.predict([np.array([indices]), np.array([segments])])[0]

        for i, token in enumerate(tokens):
            print(token, predicts[i].tolist()[:5])

 

• 获取文本向量

    def getTextEmbedding(self, _text):
        tokens = self._tokenizer.tokenize(text)
        print('Tokens:', tokens)
        indices, segments = self._tokenizer.encode(first=text, max_len=10)

        predicts = self._export_model.predict([np.array([indices]), np.array([segments])])[0]
        pooled = np.array(np.sum(predicts, axis=0))
        print('Pooled:', pooled.shape)
        return pooled

虽然传入的 text 只有 6 个字符,但是默认了 max_len = 10, 所以 tokenizer 对原句进行了 Padding,最后 pooling 的方式不局限于 sum pooling ,也可以 mean_pooling、max_pooling 等,输出的向量维度为 max_len x 768,由于后四位是 padding 得来的,如果想要更准确的向量,可以通过 mask 擦除对应 padding 的向量。

MMR 时我们可以通过传入文本的内容再 getTextEmbedding 获取文本 Embedding 然后计算向量相似度,不过这里是 Base-Bert,如果有自己文本的训练微调,效果会更好。 

6.完整代码

import numpy as np
from keras_bert import load_vocabulary, load_trained_model_from_checkpoint, Tokenizer, get_checkpoint_paths
from keras_bert.datasets import get_pretrained, PretrainedList


class Bert:
    _token_dict = None  # word ->index
    _token_dict_inv = None  # index -> word
    _model = None
    _export_model = None
    _tokenizer = None

    def __init__(self, _model_path):
        self._model_path = model_path
        self.loadBertModel(self._model_path)
        self._tokenizer = Tokenizer(self._token_dict)

    def loadBertModel(self, path):

        paths = get_checkpoint_paths(path)

        # 加载预训练模型
        self._model = load_trained_model_from_checkpoint(paths.config, paths.checkpoint, training=True, seq_len=None)
        self._export_model = load_trained_model_from_checkpoint(paths.config, paths.checkpoint, seq_len=10)
        self._model.summary(line_length=120)

        # 字典映射与反映射
        self._token_dict = load_vocabulary(paths.vocab)
        self._token_dict_inv = {v: k for k, v in self._token_dict.items()}

    # 填字
    def fillText(self, _text, _st, _end):
        tokens = self._tokenizer.tokenize(_text)

        for i in range(_st, _end + 1):
            tokens[i] = '[MASK]'
        print('Tokens:', tokens)

        # 映射后的索引、代表句子、代表Mask掩码
        indices = np.array([[self._token_dict[token] for token in tokens]])
        segments = np.array([[0] * len(tokens)])

        masks = np.array([[0] * len(tokens)])
        for i in range(_st, _end + 1):
            masks[0][i] = 1

        # 填字预测
        predicts = self._model.predict([indices, segments, masks])[0].argmax(axis=-1).tolist()
        print('Fill with: ', list(map(lambda x: self._token_dict_inv[x], predicts[0][_st:_end + 1])))

    # 两个句子是否有关联
    def hasCorrelation(self, _sentence_1, _sentence_2):
        indices, segments = self._tokenizer.encode(first=_sentence_1, second=_sentence_2)
        masks = np.array([[0] * len(indices)])

        predicts = self._model.predict([np.array([indices]), np.array([segments]), masks])[1]
        is_correlation = bool(np.argmax(predicts, axis=-1)[0])
        print('%s && %s is has Correlation: ' % (_sentence_1, _sentence_2), is_correlation)
        return is_correlation

    def getCharEmbedding(self, _text):
        tokens = self._tokenizer.tokenize(_text)
        print('Tokens:', tokens)
        indices, segments = self._tokenizer.encode(first=_text, max_len=10)
        predicts = self._export_model.predict([np.array([indices]), np.array([segments])])[0]

        for i, token in enumerate(tokens):
            print(token, predicts[i].tolist()[:5])

    def getTextEmbedding(self, _text):
        tokens = self._tokenizer.tokenize(text)
        print('Tokens:', tokens)
        indices, segments = self._tokenizer.encode(first=text, max_len=10)
        print('Indices:', indices)
        print('Segments:', segments)

        predicts = self._export_model.predict([np.array([indices]), np.array([segments])])[0]
        pooled = np.array(np.sum(predicts, axis=0))
        print('Pooled:', pooled.shape)
        return pooled


if __name__ == '__main__':
    # 获取模型路径
    model_path = get_pretrained(PretrainedList.chinese_base)

    # 加载模型
    bert = Bert(model_path)

    # 填充字符
    text = '数学是利用符号语言研究数量、结构、变化以及空间等概念的一门学科'
    st, end = 1, 2
    bert.fillText(text, st, end)
    st, end = 4, 5
    bert.fillText(text, st, end)

    # 判断前后逻辑是否一致
    sentence_1 = '数学是利用符号语言研究數量、结构、变化以及空间等概念的一門学科。'
    sentence_2 = '从某种角度看屬於形式科學的一種。'
    sentence_3 = '任何一个希尔伯特空间都有一族标准正交基。'
    bert.hasCorrelation(sentence_1, sentence_2)
    bert.hasCorrelation(sentence_1, sentence_3)

    # 获取字符 Embedding
    text = '语言模型'
    bert.getCharEmbedding(text)

    # 获取 Text Pooling 后的 Embedding
    bert.getTextEmbedding(text)

代码中给出了模型加载以及上面几个常用方法示例。

四. Fine-Tuning

Kreas-Bert 给出了基于 TPU 的 Imdb 数据的 Fine-Tuning 示例,由于原代码版本为 TF1,博主环境为 TF2,博主对代码进行了修改实现 TF2 的兼容,但是训练第一个 Epoch 时会无报错退出,这里给出代码,如果有可以跑通的同学可以评论区交流 ~

import codecs
from keras_bert import load_trained_model_from_checkpoint
import os
import numpy as np
from tqdm import tqdm
from keras_bert import Tokenizer
import tensorflow as tf

tf.compat.v1.disable_eager_execution()
SEQ_LEN = 128
BATCH_SIZE = 128
EPOCHS = 5
LR = 1e-4

if __name__ == '__main__':

    pretrained_path = './uncased_L-12_H-768_A-12'
    config_path = os.path.join(pretrained_path, 'bert_config.json')
    checkpoint_path = os.path.join(pretrained_path, 'bert_model.ckpt')
    vocab_path = os.path.join(pretrained_path, 'vocab.txt')

    # TF_KERAS must be added to environment variables in order to use TPU
    os.environ['TF_KERAS'] = '1'

    token_dict = {}
    with codecs.open(vocab_path, 'r', 'utf8') as reader:
        for line in reader:
            token = line.strip()
            token_dict[token] = len(token_dict)

        # with strategy.scope():
        model = load_trained_model_from_checkpoint(
            config_path,
            checkpoint_path,
            training=True,
            trainable=True,
            seq_len=SEQ_LEN,
        )

    dataset = tf.keras.utils.get_file(
        fname="aclImdb.tar.gz",
        origin="http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz",
        extract=True,
    )

    tokenizer = Tokenizer(token_dict)


    def load_data(path):
        global tokenizer
        indices, sentiments = [], []
        for folder, sentiment in (('neg', 0), ('pos', 1)):
            folder = os.path.join(path, folder)
            for name in tqdm(os.listdir(folder)):
                with open(os.path.join(folder, name), 'r') as reader:
                    text = reader.read()
                ids, segments = tokenizer.encode(text, max_len=SEQ_LEN)
                indices.append(ids)
                sentiments.append(sentiment)
        items = list(zip(indices, sentiments))
        np.random.shuffle(items)
        indices, sentiments = zip(*items)
        indices = np.array(indices)
        mod = indices.shape[0] % BATCH_SIZE
        if mod > 0:
            indices, sentiments = indices[:-mod], sentiments[:-mod]
        return [indices, np.zeros_like(indices)], np.array(sentiments)


    train_path = os.path.join(os.path.dirname(dataset), 'aclImdb', 'train')
    test_path = os.path.join(os.path.dirname(dataset), 'aclImdb', 'test')

    train_x, train_y = load_data(train_path)
    test_x, test_y = load_data(test_path)

    from tensorflow.python import keras
    from keras_radam import RAdam

    inputs = model.inputs[:2]
    dense = model.get_layer('NSP-Dense').output
    outputs = keras.layers.Dense(units=2, activation='softmax')(dense)

    model = keras.models.Model(inputs, outputs)
    model.compile(
        RAdam(lr=LR),
        loss='sparse_categorical_crossentropy',
        metrics=['sparse_categorical_accuracy'],
    )

    sess = tf.compat.v1.keras.backend.get_session()

    uninitialized_variables = set([i.decode('ascii') for i in sess.run(tf.compat.v1.report_uninitialized_variables())])
    init_op = tf.compat.v1.variables_initializer(
        [v for v in tf.compat.v1.global_variables() if v.name.split(':')[0] in uninitialized_variables]
    )
    sess.run(init_op)

    model.fit(
        train_x,
        train_y,
        epochs=EPOCHS,
        batch_size=BATCH_SIZE,
    )

    predicts = model.predict(test_x, verbose=True).argmax(axis=-1)
    print(np.sum(test_y == predicts) / test_y.shape[0])

五.Bert VS OpenAI GPT

论文中给出了 Bert、OpenAI GPT 以及 ELMo 的预训练模型架构差异。 BERT 使用双向 Transformer、OpenAI GPT使用从左到右的 Transformer、ELMo 使用独立训练的从左到右和从右到左的 lstm 的连接来为下游任务生成特征。在这三种表示中,只有 BERT 同时以所有层中的左右上下文为条件。除了架构差异之外,BERT 和 OpenAI GPT 是一种微调方法,而 ELMo 是一种基于特性的方法。当时对标的 GPT 其实是 GPT-2,这里提供一个简单的 demo 供大家参考:

#!/usr/bin/env Python
# coding=utf-8

from transformers import GPT2LMHeadModel, GPT2Tokenizer
import torch

# 初始化GPT2模型的Tokenizer类.
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
# 初始化GPT2模型, 此处以初始化GPT2LMHeadModel()类的方式调用GPT2模型.
model = GPT2LMHeadModel.from_pretrained('gpt2')

# GPT模型第一次迭代的输入的上下文内容, 将其编码以序列化.同时, generated也用来存储GPT2模型所有迭代生成的token索引.
generated = tokenizer.encode("The Tower bridge")  # generated: [11708] => [11708, 13]
# 将序列化后的第一次迭代的上下文内容转化为pytorch中的tensor形式.
context = torch.tensor([generated])  # context: tensor([[11708]])
# 第一次迭代时还无past_key_values元组.
past_key_values = None

iter_num = 30

for i in range(iter_num):
    '''
    此时模型model返回的output为CausalLMOutputWithPastAndCrossAttentions类,
    模型返回的logits以及past_key_values对象为其中的属性,
    CausalLMOutputWithPastAndCrossAttentions(
            loss=loss, # tensor([[[-34.2719, -33.7875, -36.4804,  ..., -40.9417, -40.9629, -33.9127]]], grad_fn=<UnsafeViewBackward0>)
            logits=lm_logits,
            past_key_values=transformer_outputs.past_key_values, # 
            hidden_states=transformer_outputs.hidden_states,
            attentions=transformer_outputs.attentions,
            cross_attentions=transformer_outputs.cross_attentions,
        )
'''

    output = model(context, past_key_values=past_key_values)
    past_key_values = output.past_key_values
    # 此时获取GPT2模型计算的输出结果hidden_states张量中第二维度最后一个元素的argmax值, 得出的argmax值即为此次GPT2模型迭代
    # 计算生成的下一个token. 注意, 此时若是第一次迭代, 输出结果hidden_states张量的形状为(batch_size, sel_len, n_state);
    # 此时若是第二次及之后的迭代, 输出结果hidden_states张量的形状为(batch_size, 1, n_state), all_head_size=n_state=nx=768.
    logits = output.logits
    v = logits[..., -1, :]
    token = torch.argmax(output.logits[..., -1, :])  # token: tensor(13)

    # 将本次迭代生成的token的张量变为二维张量, 以作为下一次GPT2模型迭代计算的上下文context.
    context = token.unsqueeze(0)
    # 将本次迭代计算生成的token的序列索引变为列表存入generated
    generated += [token.tolist()]

# 将generated中所有的token的索引转化为token字符.
sequence = tokenizer.decode(generated)
sequence = sequence.split(".")[:-1]
print(sequence)

给定 'The Tower Bridge' 和迭代次数,GPT-2 会不断迭代并通过 argmax 获取概率最大的词并 += 添加至 generated 中,最后将 generated decode 得到文本:

['The Tower bridge is a great place to get to know the city', " It's a great place to get to know the city"]

六.总结

昨天 MMR 算法中提到了 Bert 生成文本向量,今天趁热打铁简单介绍下 Bert。工业场景下,可以利用 Bert 和 GPT 进行业务场景的微调并应用于文案、文案 Embedding 生成等任务。

参考:

google-research - bert

Pre-training of Deep Bidirectional Transformers for Language Understanding

CyberZHG / keras-bert Public

更多推荐算法相关深度学习:深度学习导读专栏 

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

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

相关文章

chatgpt赋能python:Python中等待一秒的语句:让你的程序暂停与等待

Python中等待一秒的语句&#xff1a;让你的程序暂停与等待 当编写Python程序时&#xff0c;经常需要添加暂停或延迟功能&#xff0c;以使程序能够在执行某些操作之前或之后等待一段时间。Python拥有一个内置的语句可以实现这种延迟&#xff1a;time.sleep()。 什么是 time.sl…

四信大气环保远程监测平台,实现大气网格化、精准化监测

近年来&#xff0c;随着国民经济快速发展&#xff0c;我国工业化、城镇化进程加快&#xff0c;随之造成的大气污染问题日益严峻&#xff0c;严重影响人们日常生活幸福指数与身体健康。为此&#xff0c;中共中央、国务院先后发布一系列文件&#xff0c;要求加强大气环境监测&…

价格限制与经济福利

价格控制 实行价格控制通常是政府相信市场价格对买方或卖方不公平两种价格控制&#xff1a;价格上限和价格下限 价格上限&#xff1a; 法定最高价格&#xff0c;任何人不得收取或付出高于此的价格。 价格下限&#xff1a; 法定最低价格&#xff0c;任何人不得收取或付出低于…

为什么亚马逊股价会在今年上涨?亚马逊股价2023年还会继续上涨吗?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 为什么亚马逊的股价会上涨&#xff1f; 今年以来&#xff0c;亚马逊&#xff08;AMZN&#xff09;的股价已经上涨了20%以上&#xff0c;涨幅达到了23.3%。而同期标普500指数今年以来仅上涨了8.2%。 猛兽财经认为&#xff0c…

数据链路层:媒体接入控制

1.数据链路层&#xff1a;媒体接入控制 笔记来源&#xff1a; 湖科大教书匠&#xff1a;媒体接入控制的基本概念 湖科大教书匠&#xff1a;随机接入–CSMA/CD协议 湖科大教书匠&#xff1a;随机接入–CSMA/CA协议 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记…

微信小程序——监听页面滑动(二)判断用户在做向上滑动还是向下滑动(onScrollPage scroll-view)

手把手教你学会判断用户在做向上滑动还是向下滑动 知识回调&#xff08;不懂就看这儿&#xff01;&#xff09;场景复现核心干货onPageScroll结合scrollTop实现了解touch方法 知识回调&#xff08;不懂就看这儿&#xff01;&#xff09; 知识专栏专栏链接微信小程序专栏https:…

1.WebGL与Shader介绍

webgl介绍 WebGL是一种用于在网页浏览器中创建交互式3D图形的技术。它基于OpenGL ES 2.0&#xff0c;这是一个广泛使用的嵌入式系统3D图形API。以下是webgl的发展史&#xff1a; WebGL允许开发人员使用JavaScript编写代码来控制GPU&#xff08;图形处理单元&#xff09;&…

字符集、字符编码格式检测和转码

目录 1 locale与字符集 1.1 locale 1.2 字符集 2 常见字符集 2.1 Native ANSI 字符集 2.1.1 ASCII 2.1.2 ISO-8859-1 2.1.3 GB2312&#xff0c;GBK&#xff0c;GB18030 2.2 Unicode 字符集 2.2.1 UCS 2.2.2 UTF - Unicode Transformation Format 2.2.3 UTF-8 2.2.4 B…

Spring Boot如何实现分布式消息队列

Spring Boot如何实现分布式消息队列 在分布式系统中&#xff0c;消息队列是非常重要的一部分&#xff0c;可以帮助开发人员实现异步处理、解耦系统、提高系统可靠性等。本文将介绍如何使用 Spring Boot 实现分布式消息队列。 1. 消息队列的设计 消息队列是一种存储消息的容器…

一键禁掉WIN10自动更新

工作了很久&#xff0c;没备份睡觉去了&#xff0c;一觉起来我东西呢&#xff1f; 正玩着游戏&#xff0c;激战正嗨的时候&#xff0c;蓝屏转圈圈开始更新。 无数次搜索怎么去除WIN10自动更新&#xff0c;每次按照网上的教程操作&#xff0c;结果都是无功而返。 下载了很多工…

Java开发 - 让你少走弯路的Redis主从实现单节点哨兵模式

前言 前一篇中&#xff0c;我们讲解了Redis主从的搭建方式&#xff0c;其实很简单呐有木有&#xff0c;都是配置&#xff0c;连句代码都没有&#xff0c;是不是感觉高估了Redis主从的搭建方式&#xff1f;哈哈&#xff0c;没关系&#xff0c;跟着博主&#xff0c;包你全会。今…

Postgre 提示could not determine data type of parameter $4

目录 场景&#xff1a; 现象&#xff1a; 版本&#xff1a; 分析&#xff1a; 解决方式&#xff1a; 场景&#xff1a; 今天遇到现场环境连接Postgre数据库&#xff0c;日志提示could not determine data type of parameter $4&#xff0c;通过日志复制出完整sql&#xff…

软件测试练手项目,可以写进简历里面的(银行:金融:商城:外卖等等)

目录 一、引言 二、测试任务 三、测试进度 四、测试资源 五、测试策略 六、测试完成标准 七、风险和约束 八、问题严重程度描述和响应时间规范 九、测试的主要角色和职责 ​有需要实战项目的评论区留言吧&#xff01; 软件测试是使用人工或者自动的手段来运行或者测定…

旅游有哪些好玩的地方? 今天用python分析适合年轻人的旅游攻略

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 “旅”是旅行&#xff0c;外出&#xff0c;即为了实现某一目的而在空间上从甲地到乙地的行进过程&#xff1b; “游”是外出游览、观光、娱乐&#xff0c;即为达到这些目的所作的旅行。 二者合起来即旅游。所以&#…

文本三剑客——awk

文本编辑器awk 一、 awk工作原理1.命令格式2.awk常见的内建变量&#xff08;可直接用&#xff09;如下所示 二、awk的基础用法1.输出文件中的某一列2.根据特定条件筛选数据3.按照分隔符进行切割4.在匹配到特定字符串时执行操作5.BEGIN打印模式6.awk的分隔符用法 三、示例演示1.…

挖掘数百篇AR/VR专利,苹果XR全方位探索

近期大家也看了很多苹果XR的消息&#xff0c;与其看各种爆料、不如从专利入手来看看苹果XR头显可能会有哪些不同的玩法。于是&#xff0c;我们就从苹果近年来有关AR/VR的数百篇专利中&#xff0c;选出可能会直接决定和影响XR产品体验的部分汇总出来。当然&#xff0c;这些专利不…

智警杯赛前学习2.1--sql概述

绝大多数分析岗位&#xff0c;需要sql功能 sql是架起通往其它工具的桥梁 DDL数据定义语言 DML数据操纵语言&#xff0c;主要包括&#xff1a;insert&#xff0c;delete&#xff0c;update DQL数据查询语言 DCL数据库控制语言 关系型数据库&#xff0c;类似excel的二维表 …

windows中同时安装两个不同版本的mysql

文章目录 前言解压mysql新建所需目录及文件配置my.ini文件安装配置mysql8.0.25dll丢失错误解决问题 修改注册表启动mysql登录后修改密码总结 前言 有的时候&#xff0c;你是用的mysql版本和公司使用的mysql版本不一致&#xff0c;这样就会导致你不得不在你的电脑上安装两个版本…

Ansible基础四——变量、机密、事实

文章目录 一、变量二、机密2.1 创建加密文件2.2 查看加密文件2.3 编辑加密文件内容2.4 加密现有文件2.5 解密文件2.6 更改加密密码 三、事实3.1 收集展示事实3.2 展示某个结果3.3 新旧事实命令3.4 关闭事实3.5 魔法变量 一、变量 常设置的变量&#xff1a; 要创建的用户要安装的…

设计模式之~中介者模式

简述&#xff1a; 中介者模式又叫做调停者模式。其实就是中间人或者调停者的意思。 中介者模式&#xff08;Mediator&#xff09;&#xff0c;用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互调用&#xff0c;从而式其耦合松散&#xff0c;而且可以独立…