LBERT论文详解

news2024/9/28 23:26:35

论文地址:https://arxiv.org/abs/2105.07148

代码地址:https://github.com/liuwei1206/LEBERT

模型创新

  1. LEBRT采用句子中的词语对(论文中称为Char-Word Pair)的特征作为输入
  2. 作者设计Lexicon adapter,在BERT的中间某一层注入词汇特征

  • 左图是在BERT之后的架构上面引入词汇信息

  • 右图是在BERT底层时引入词汇信息

Char-Word Pair

首先,对于给定的中文句子
s c = { c 1 , c 2 , . . . , c n } c i 代表句子中的第 i 个字符 s_c = \{c_1,c_2,...,c_n\}\quad c_i代表句子中的第i个字符 sc={c1,c2,...,cn}ci代表句子中的第i个字符
利用词典D匹配出句子中包含的潜在词汇(这里设定最多匹配出相关性最强的三个,不足三个则用PAD填充)。然后,每个字符和包含该字符的词汇组成词汇对,表示为
s c w = { ( c 1 , w s 1 ) , ( c 2 , w s 2 ) , . . . , ( c n , w s n ) } w s i 表示包含 c i 词汇组成的集合 s_{cw} = \{(c_1,ws_1),(c_2,ws_2),...,(c_n,ws_n)\}\quad ws_i表示包含c_i词汇组成的集合 scw={(c1,ws1),(c2,ws2),...,(cn,wsn)}wsi表示包含ci词汇组成的集合
此时就构成了Char-Words Pair Sequence

Lexicon adapter

将输入数据构建成Char-Words Pair Sequence后,句子中的每个位置包含了字符特征和词汇特征,为了把词汇特征注入到BERT中,作者设计了Lexicon adapter

Char-Words Pair Sequence中的第i个位置char-words pair表示为 ( h i c , x i w s ) (h_i^c,x_i^{ws}) (hic,xiws)

  • h i c h_i^c hic:第i个位置的字符特征,该特征是BERT的某个Transformer层的输出
  • x i w s = { x i 1 w , x i 2 w , . . . , x i m w } x_i^{ws} = \{x_{i1}^w,x_{i2}^w,...,x_{im}^w\} xiws={xi1w,xi2w,...,ximw} :第i个位置字符对应m个词汇的词向量

对词向量使用非线性变换,以至于和字符向量进行维度对齐
v i j w = W 2 ( t a n h ( W 1 x i j w + b 1 ) ) + b 2 j = 1 , . . . , m v_{ij}^w = W_2(tanh(W_1x_{ij}^w + b_1)) + b_2\quad\quad j=1,...,m vijw=W2(tanh(W1xijw+b1))+b2j=1,...,m

  • d c d_c dc:字符特征维度
  • d w d_w dw:词向量维度
  • W 1 ∈ R d c ∗ d w W_1\in R ^{d_c*d_w} W1Rdcdw
  • W 2 ∈ R d c ∗ d c W_2\in R ^{d_c*d_c} W2Rdcdc
  • v i j w ∈ R d c v_{ij}^w \in R^{d_c} vijwRdc

此时,对于Char-Words Pair Sequence中的第i个位置,进行维度变换后的词向量集合为
V i = ( v i 1 w , . . . , v i m w ) ∈ R m ∗ d c V_i = (v_{i1}^w,...,v_{im}^w)\in R^{m*d_c} Vi=(vi1w,...,vimw)Rmdc
此时,使用注意力机制对 V i V_i Vi中的m个词向量进行融合
a i = s o f t m a x ( h i c W a t t n V i T ) a_i = softmax(h_i^cW_{attn}V_i^T) ai=softmax(hicWattnViT)

  • h i c h_i^c hic:为query向量
  • V i V_i Vi:为value
  • a i a_i ai:使用双线性变换矩阵计算相似度得分得到

之后,再利用相似度得分对 V i V_i Vi进行加权求和得到融合后词特征:
z i w = ∑ j = 1 m a i j v i j w z_i^w = \sum_{j=1}^m a_{ij}v_{ij}^w ziw=j=1maijvijw
最后,再把字符特征和融合后的词特征相加得到:
h ^ i = h i c + z i w \hat h_i^ = h_i^c + z_i^w h^i=hic+ziw

Lexicon Enhanced BERT

对于给定的中文句子 s c = { c 1 , c 2 , . . . , c n } s_c = \{c_1,c_2,...,c_n\} sc={c1,c2,...,cn},将其构建成character-words pair sequence形式
s c w = { ( c 1 , w s 1 ) , ( c 2 , w s 2 ) , . . . , ( c n , w s n ) } s_{cw} = \{(c_1,ws_1),(c_2,ws_2),...,(c_n,ws_n)\} scw={(c1,ws1),(c2,ws2),...,(cn,wsn)}
{ c 1 , c 2 , . . . , c n } \{c_1,c_2,...,c_n\} {c1,c2,...,cn}输入到BERT的Input Embedder当中,得到输出 E = { e 1 , e 2 , . . . , e n } E = \{e_1,e_2,...,e_n\} E={e1,e2,...,en},之后将E($H^0 = E $)输入到BERT的Transformer encoder中,每个Transformer encoder表示为如下形式:
$$
G &= Layernormalization(H^{l-1 } + Multiheadattention(H^{l-1}))\

H &= Layernormalization(G + FFN(G))
$$
之后,通过Lexicon Adapter把词汇信息注入到第k层和第k+1层的Transformer层之间

第k层Transformer层的输出为 H k = { h 1 k , h 2 k , . . . , h n k } H^k = \{h_1^k,h_2^k,...,h_n^k\} Hk={h1k,h2k,...,hnk}。将其中的每一个Char-Words Pair( h i k , x i w s h_i^k,x_i^{ws} hik,xiws)利用Lexicon Adapter进行转化得到:
h ^ k = L A ( h i k , x i w s ) \hat h^k = LA(h_i^k,x_i^{ws}) h^k=LA(hik,xiws)

代码讲解

词向量处理

加载词向量(load_word_embedding)

input

  • word_embed_path:词向量的路径,这里选取腾讯的tencent-ailab-embedding-zh-d200-v0.2.0-s.txt
    • 词的个数为2000000,向量维度为200
  • max_scan_num:最多加载多少个词向量

output

  • word_embed_dict:每个词对应的词向量 200000 * 200
  • word_list:词集合,2000000
  • word_embed_dim:词的维度,200
    @classmethod
    def build_trie_tree(cls, word_list, save_path):
        """
        # todo 是否不将单字加入字典树中
        构建字典树
        :return:
        """
        logger.info('building trie tree')
        trie_tree = Trie()
        for word in word_list:
            trie_tree.insert(word)
        write_pickle(trie_tree, save_path)
        return trie_tree
    def load_word_embedding(cls, word_embed_path, max_scan_num):
        """
        todo 存在许多单字的,考虑是否去掉
        """
        logger.info('loading word embedding from pretrain')
        word_embed_dict = dict()
        word_list = list()
        with open(word_embed_path, 'r', encoding='utf8') as f:
            for idx, line in tqdm(enumerate(f)):
                # 只扫描前max_scan_num个词向量
                if idx > max_scan_num:
                    break
                items = line.strip().split()
                if idx == 0:
                    assert len(items) == 2
                    num_embed, word_embed_dim = items
                    num_embed, word_embed_dim = int(num_embed), int(word_embed_dim)
                else:
                    assert len(items) == word_embed_dim + 1
                    word = items[0]
                    embedding = np.empty([1, word_embed_dim])
                    embedding[:] = items[1:]
                    word_embed_dict[word] = embedding
                    word_list.append(word)
        logger.info('word_embed_dim:{}'.format(word_embed_dim))
        logger.info('size of word_embed_dict:{}'.format(len(word_embed_dict)))
        logger.info('size of word_list:{}'.format(len(word_list)))

        return word_embed_dict, word_list, word_embed_dim
构建字典树(build_trie_tree)

input

  • word_list:word_list:词集合,2000000
  • save_path:字典树的保存路径(方便复用)

output

  • trie_tree:字典树
    @classmethod
    def build_trie_tree(cls, word_list, save_path):
        """
        # todo 是否不将单字加入字典树中
        """
        logger.info('building trie tree')
        trie_tree = Trie()
        for word in word_list:
            trie_tree.insert(word)
        write_pickle(trie_tree, save_path)
        return trie_tree
    
        def write_pickle(x, path):
            with open(path, 'wb') as f:
                pickle.dump(x, f)
找到数据集中的所有单词(get_words_from_corpus)

input

  • files:训练、验证、测试的文件路径
  • save_file:文件保存路径
  • trie_tree:字典树

output

  • all_matched_words:找到了所有跟我们训练、验证、测试数据有关的所有词
    @classmethod
    def get_words_from_corpus(cls, files, save_file, trie_tree):
        """
        找出文件中所有匹配的单词
        """
        logger.info('getting words from corpus')
        all_matched_words = set()
        for file in files:
            with open(file, 'r', encoding='utf8') as f:
                lines = f.readlines()
                for idx in trange(len(lines)):
                    line = lines[idx].strip()
                    data = json.loads(line)
                    text = data['text']
                    matched_words = cls.get_words_from_text(text, trie_tree)
                    _ = [all_matched_words.add(word) for word in matched_words]

        all_matched_words = list(all_matched_words)
        all_matched_words = sorted(all_matched_words)
        write_lines(all_matched_words, save_file)
        return all_matched_words
    
    @classmethod
    def get_words_from_text(cls, text, trie_tree):
        """
        找出text中所有的单词
        """
        length = len(text)
        matched_words_set = set()   # 存储匹配到的单词
        for idx in range(length):
            sub_text = text[idx:idx + trie_tree.max_depth]
            words = trie_tree.enumerateMatch(sub_text)

            _ = [matched_words_set.add(word) for word in words]
        matched_words_set = list(matched_words_set)
        matched_words_set = sorted(matched_words_set)
        return matched_words_set
    
def write_lines(lines, path, encoding='utf8'):
    with open(path, 'w', encoding=encoding) as f:
        for line in lines:
            f.writelines('{}\n'.format(line))
初始化模型的词向量(init_model_word_embedding)

input

  • corpus_words:所有跟我们训练、验证、测试数据有关的所有词
  • word_embed_dict:每个词对应的词向量 200000 * 200
  • save_embed_path:词向量的保存路径
  • save_word_vocab_path:词表的保存保存

output

  • model_word_embedding:模型的嵌入向量 --> 20857 * 200
  • word_vocab:模型的词表 --> 20857
  • embed_dim:嵌入维度 --> 200
    def init_model_word_embedding(self, corpus_words, word_embed_dict, save_embed_path, save_word_vocab_path):
        logger.info('initializing model word embedding')
        # 构建单词和id的映射
        word_vocab = Vocabulary(corpus_words, vocab_type='word')
        # embed_dim = len(word_embed_dict.items()[1].size)
        embed_dim = next(iter(word_embed_dict.values())).size

        scale = np.sqrt(3.0 / embed_dim)
        model_word_embedding = np.empty([word_vocab.size, embed_dim])

        matched = 0
        not_matched = 0

        for idx, word in enumerate(word_vocab.idx2token):
            if word in word_embed_dict:
                model_word_embedding[idx, :] = word_embed_dict[word]
                matched += 1
            else:
                model_word_embedding[idx, :] = np.random.uniform(-scale, scale, [1, embed_dim])
                not_matched += 1

        logger.info('num of match:{}, num of not_match:{}'.format(matched, not_matched))
        write_pickle(model_word_embedding, save_embed_path)
        write_pickle(word_vocab, save_word_vocab_path)

        return model_word_embedding, word_vocab, embed_dim

数据加载格式

每个汉字对应的单词列表(get_char2words)

input:

  • text:文本

output:

  • char_index2words:文本中每个汉字所对应的词
    def get_char2words(self, text):
        """
        获取每个汉字,对应的单词列表
        """
        text_len = len(text)
        char_index2words = [[] for _ in range(text_len)]

        for idx in range(text_len):
            sub_sent = text[idx:idx + self.trie_tree.max_depth]  # speed using max depth
            words = self.trie_tree.enumerateMatch(sub_sent)  # 找到以text[idx]开头的所有单词
            for word in words:
                start_pos = idx
                end_pos = idx + len(word)
                for i in range(start_pos, end_pos):
                    char_index2words[i].append(word)
        return char_index2words
数据加载格式(collate)

output

  • 特征输入为:
'text': text, 
'input_ids': input_ids, 
'attention_mask': input_mask, 
'token_type_ids': token_type_ids,
'word_ids': word_ids, 
'word_mask': word_mask, 
'label_ids': label_ids
    def get_input_data(self, file):
        lines = load_lines(file)
        features = []
        cls_token_id = self.tokenizer.cls_token_id
        sep_token_id = self.tokenizer.sep_token_id
        pad_token_id = self.tokenizer.pad_token_id
        o_label_id = self.label_vocab.convert_token_to_id('O')
        pad_label_id = self.label_vocab.convert_token_to_id('[PAD]')

        for line in tqdm(lines):
            data = json.loads(line)
            text = data['text']
            labels = data['label']
            char_index2words = self.get_char2words(text)

            # 在开头与结尾分别添加[CLS]与[SEP]
            input_ids = [cls_token_id] + self.tokenizer.convert_tokens_to_ids(text) + [sep_token_id]
            label_ids = [o_label_id] + self.label_vocab.convert_tokens_to_ids(labels) + [o_label_id]

            word_ids_list = []
            word_pad_id = self.word_vocab.convert_token_to_id('[PAD]')
            for words in char_index2words:
                words = words[:self.max_word_num]
                word_ids = self.word_vocab.convert_tokens_to_ids(words)
                word_pad_num = self.max_word_num - len(words)
                word_ids = word_ids + [word_pad_id] * word_pad_num
                word_ids_list.append(word_ids)
            # 开头和结尾进行padding
            word_ids_list = [[word_pad_id]*self.max_word_num] + word_ids_list + [[word_pad_id]*self.max_word_num]

            if len(input_ids) > self.max_seq_len:
                input_ids = input_ids[: self.max_seq_len]
                label_ids = label_ids[: self.max_seq_len]
                word_ids_list = word_ids_list[: self.max_seq_len]
            input_mask = [1] * len(input_ids)
            token_type_ids = [0] * len(input_ids)
            assert len(input_ids) == len(label_ids) == len(word_ids_list)

            # padding
            padding_length = self.max_seq_len - len(input_ids)
            input_ids += [pad_token_id] * padding_length
            input_mask += [0] * padding_length
            token_type_ids += [0] * padding_length
            label_ids += [pad_label_id] * padding_length
            word_ids_list += [[word_pad_id]*self.max_word_num] * padding_length

            text = ''.join(text)
            input_ids = torch.LongTensor(input_ids)
            label_ids = torch.LongTensor(label_ids)
            input_mask = torch.LongTensor(input_mask)
            token_type_ids = torch.LongTensor(token_type_ids)
            word_ids = torch.LongTensor(word_ids_list)
            word_mask = (word_ids != word_pad_id).long()

            feature = {
                'text': text, 'input_ids': input_ids, 'attention_mask': input_mask, 'token_type_ids': token_type_ids,
                'word_ids': word_ids, 'word_mask': word_mask, 'label_ids': label_ids
            }
            features.append(feature)

        return features

模型运行

模型初步加载
  • config.word_vocab_size:20857
  • config.word_embed_dim:200
  • LEBertModel:对其中的BertEncoder模块进行改造,后续会详细介绍
class LEBertSoftmaxForNer(BertPreTrainedModel):
    def __init__(self, config):
        super(LEBertSoftmaxForNer, self).__init__(config)
        self.word_embeddings = nn.Embedding(config.word_vocab_size, config.word_embed_dim)
        self.num_labels = config.num_labels
        self.bert = LEBertModel(config)
        self.dropout = nn.Dropout(config.hidden_dropout_prob)
        self.classifier = nn.Linear(config.hidden_size, config.num_labels)
        self.loss_type = config.loss_type
        self.init_weights()

    def forward(self, input_ids, attention_mask, token_type_ids, word_ids, word_mask, ignore_index, labels=None):
        word_embeddings = self.word_embeddings(word_ids)
        outputs = self.bert(
            input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids,
            word_embeddings=word_embeddings, word_mask=word_mask
        )
        sequence_output = outputs[0]
        sequence_output = self.dropout(sequence_output)
        logits = self.classifier(sequence_output)
        outputs = (logits,) + outputs[2:]  # add hidden states and attention if they are here
        if labels is not None:
            assert self.loss_type in ['lsr', 'focal', 'ce']
            if self.loss_type == 'lsr':
                loss_fct = LabelSmoothingCrossEntropy(ignore_index=ignore_index)
            elif self.loss_type == 'focal':
                loss_fct = FocalLoss(ignore_index=ignore_index)
            else:
                loss_fct = CrossEntropyLoss(ignore_index=ignore_index)
            # Only keep active parts of the loss
            if attention_mask is not None:
                active_loss = attention_mask.contiguous().view(-1) == 1
                active_logits = logits.contiguous().view(-1, self.num_labels)[active_loss]
                active_labels = labels.contiguous().view(-1)[active_loss]
                loss = loss_fct(active_logits, active_labels)
            else:
                loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
            outputs = (loss,) + outputs
        return outputs  # (loss), scores, (hidden_states), (attentions)

model.word_embeddings.weight.data.copy_(torch.from_numpy(processor.word_embedding))

  • 把词向量的word_embedding赋给LEBertSoftmaxForNer
简要概述
class LEBertModel(BertPreTrainedModel):
    def __init__(self, config):
        self.encoder = BertEncoder(config)
    
class BertEncoder(nn.Module):
	def __init__(self, config):
        super().__init__()
        self.config = config
        self.layer = nn.ModuleList([BertLayer(config) for _ in range(config.num_hidden_layers)])
        self.word_embedding_adapter = WordEmbeddingAdapter(config)
        
     def forward():
		# 在第i层之后,进行融合
		if i == self.config.add_layer:
                hidden_states = self.word_embedding_adapter(hidden_states, word_embeddings, word_mask)
核心代码
class WordEmbeddingAdapter(nn.Module):
    
    def __init__(self, config):
        super(WordEmbeddingAdapter, self).__init__()
        self.dropout = nn.Dropout(config.hidden_dropout_prob)
        self.tanh = nn.Tanh()

        self.linear1 = nn.Linear(config.word_embed_dim, config.hidden_size)
        self.linear2 = nn.Linear(config.hidden_size, config.hidden_size)

        attn_W = torch.zeros(config.hidden_size, config.hidden_size)
        self.attn_W = nn.Parameter(attn_W)
        self.attn_W.data.normal_(mean=0.0, std=config.initializer_range)
        self.layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)

    def forward(self, layer_output, word_embeddings, word_mask):
        """
        :param layer_output:bert layer的输出,[b_size, len_input, d_model]
        :param word_embeddings:每个汉字对应的词向量集合,[b_size, len_input, num_word, d_word]
        :param word_mask:每个汉字对应的词向量集合的attention mask, [b_size, len_input, num_word]
        """

        # transform
        # 将词向量,与字符向量进行维度对齐
        word_outputs = self.linear1(word_embeddings)
        word_outputs = self.tanh(word_outputs)
        word_outputs = self.linear2(word_outputs)
        word_outputs = self.dropout(word_outputs)   # word_outputs:[b_size, len_input, num_word, d_model]

        # 计算每个字符向量,与其对应的所有词向量的注意力权重,然后加权求和。采用双线性映射计算注意力权重
        # layer_output = layer_output.unsqueeze(2)    # layer_output:[b_size, len_input, 1, d_model]
        socres = torch.matmul(layer_output.unsqueeze(2), self.attn_W)  # [b_size, len_input, 1, d_model]
        socres = torch.matmul(socres, torch.transpose(word_outputs, 2, 3))  # [b_size, len_input, 1, num_word]
        socres = socres.squeeze(2)  # [b_size, len_input, num_word]
        socres.masked_fill_(word_mask, -1e9)  # 将pad的注意力设为很小的数
        socres = F.softmax(socres, dim=-1)  # [b_size, len_input, num_word]
        attn = socres.unsqueeze(-1)  # [b_size, len_input, num_word, 1]

        weighted_word_embedding = torch.sum(word_outputs * attn, dim=2)  # [N, L, D]   # 加权求和,得到每个汉字对应的词向量集合的表示
        layer_output = layer_output + weighted_word_embedding

        layer_output = self.dropout(layer_output)
        layer_output = self.layer_norm(layer_output)

        return layer_output

 # transform
        # 将词向量,与字符向量进行维度对齐
        word_outputs = self.linear1(word_embeddings)
        word_outputs = self.tanh(word_outputs)
        word_outputs = self.linear2(word_outputs)
        word_outputs = self.dropout(word_outputs)   # word_outputs:[b_size, len_input, num_word, d_model]

        # 计算每个字符向量,与其对应的所有词向量的注意力权重,然后加权求和。采用双线性映射计算注意力权重
        # layer_output = layer_output.unsqueeze(2)    # layer_output:[b_size, len_input, 1, d_model]
        socres = torch.matmul(layer_output.unsqueeze(2), self.attn_W)  # [b_size, len_input, 1, d_model]
        socres = torch.matmul(socres, torch.transpose(word_outputs, 2, 3))  # [b_size, len_input, 1, num_word]
        socres = socres.squeeze(2)  # [b_size, len_input, num_word]
        socres.masked_fill_(word_mask, -1e9)  # 将pad的注意力设为很小的数
        socres = F.softmax(socres, dim=-1)  # [b_size, len_input, num_word]
        attn = socres.unsqueeze(-1)  # [b_size, len_input, num_word, 1]

weighted_word_embedding = torch.sum(word_outputs * attn, dim=2) 

layer_output = layer_output + weighted_word_embedding

参照资料

论文解说:https://zhuanlan.zhihu.com/p/374720213

复现代码:https://github.com/yangjianxin1/LEBERT-NER-Chinese

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

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

相关文章

Codeforces Round 839 (Div. 3)E题解

文章目录 [Permutation Game](https://codeforces.com/contest/1772/problem/E)问题建模问题分析1.分析一个玩家想要获胜的关键2.分析阻塞元素的类别3.分析阻塞元素的类别对于局面的影响代码 Permutation Game 问题建模 给定一个长度为n的排列,排列的每个元素都被阻…

CentOS 7安装PostgreSQL 15版本数据库

目录 一、何为PostgreSQL? 二、PostgreSQL安装 2.1安装依赖 2.2 执行安装 2.3 数据库初始化 2.4 配置环境变量 2.5 创建数据库 2.6 配置远程 2.7 测试远程 三、常用命令 四、用户创建和数据库权限 一、何为PostgreSQL? PostgreSQL是以加州大学…

DGNN Survey

Dynamic Graph Definition G ( V , E , X ) G (V, E, X) G(V,E,X) V v 1 , v 2 , . . . , v m V {v_1, v_2, ..., v_m} Vv1​,v2​,...,vm​ E e i , j E {e_{i, j}} Eei,j​ , e i , j ( v i , v j , f i , j ) e_{i,j} (v_i, v_j, f_{i,j}) ei,j​(vi​,vj​,fi,j​…

M1/M2 通过VM Fusion安装Win11 ARM,解决联网和文件传输

前言 最近新入了Macmini M2,但是以前的老电脑的虚拟机运行不起来了。😅,实际上用过K8S的时候,会发现部分镜像也跑不起来,X86的架构和ARM实际上还是有很多隐形兼容问题。所以只能重新安装ARM Win11,幸好微软…

【初阶C语言】整数比大小

各位大佬的光临已是上上签 在C语言刷题过程中,一定遇到过很多比大小的题目,那么本节就专门介绍比大小的方法,若大佬们还有更优解,欢迎补充呀! 本节讲解的方法主要有三种:1.条件判断 2.三目操作符 3.函数调…

JA64 1+2+3+...n

一、题目 求123...n_牛客题霸_牛客网 二、代码 1.使用静态成员变量构造函数 class SUM {private:static int _i;static int _ret;public:SUM(){_ret _ret _i;_i;}static int GetRet(){return _ret;} }; int SUM::_i1; int SUM::_ret0;class Solution { public:int Sum_So…

暴力求解--完数个数(等于本身之外的因子之和)

找出10000以内的自然数中的所有完数&#xff0c;并统计找到的完数个数。 #include<stdio.h> int main() {//找到10000以内所有的完数&#xff08;等于恰好等于它本身之外的因子之和&#xff09;&#xff0c;并统计完数个数。int n,i,s,count0;printf("找到的所有完…

jsonp 实现跨域 同时也是一个 webflux 的demo 示例

文章目录 核心原理代码html服务端 &#xff08;java 为例子&#xff09;服务端目录结构 核心原理 前端&#xff1a; 使用js 创建 script 标签&#xff0c;将请求地址&#xff0c;放到其src 中&#xff0c;并将 script 标签追加到文档流&#xff1b;后端&#xff1a;根据约定好…

Latex好看的引用(文献,url, 文内引用)

强迫症实锤了&#xff0c;完全符合本人审美&#xff01;&#xff01;&#xff01; \usepackage{hyperref} \hypersetup{ hidelinks, colorlinkstrue, linkcolorIndigo, urlcolorDeepSkyBlue4, citecolorIndigo }基本还原了 哼&#xff0c;欺负老子色彩妹那么敏感是吧&…

WIZnet W51000S-EVB-PICO 入门教程(一)

概述 W5100S-EVB-Pico是基于树莓派RP2040和全硬件TCP/IP协议栈控制器W5100S的微控制器开发板-基本上与树莓派Pico板相同&#xff0c;但通过W5100S芯片增加了以太网功能。 W5100S-EVB-Pico特点 RP2040规格参数 双核Arm Cortex-M0 133MHz264KB 高速SRAM和2MB板载内存通过…

Docker复杂命令便捷操作

启动所有状态为Created的容器 要启动所有状态为"created"的Docker容器&#xff0c;可以使用以下命令&#xff1a; docker container start $(docker container ls -aq --filter "statuscreated")上述命令执行了以下步骤&#xff1a; docker container l…

【Linux】-进程概念及初始fork

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

C++ 拷贝构造函数

拷贝构造函数是一种特殊的构造函数&#xff0c;具有一般构造函数的所有特性&#xff0c;其形参是本类的对象的引用。其作用是使用一个已经存在的对象&#xff08;由拷贝构造函数的参数指定&#xff09;&#xff0c;去初始化同类的一个新对象。 如果程序员没有定义类的拷贝构造函…

自动驾驶感知系统--惯性导航定位系统

惯性导航定位 惯性是所有质量体本身的基本属性&#xff0c;所以建立在牛顿定律基础上的惯性导航系统&#xff08;Inertial Navigation System,INS&#xff09;(简称惯导系统)不与外界发生任何光电联系&#xff0c;仅靠系统本身就能对车辆进行连续的三维定位和三维定向。卫星导…

Ubuntu-文件和目录相关命令一

&#x1f52e;linux的文件系统结构 ⛳目录结构及目录路径 &#x1f9e9;文件系统层次结构标准FHS Filesystem Hierarchy Standard(文件系统层次结构标准&#xff09; Linux是开源的软件&#xff0c;各Linux发行机构都可以按照自己的需求对文件系统进行裁剪&#xff0c;所以众多…

MyBatisPlus从入门到精通-3

紧接着上一篇的查询 接下来的重点介绍增删改操作了 Insert id&#xff08;主键&#xff09;生成策略 前面的案列中我们没有指定id字段 但是它是生成了一个很长的id&#xff0c;并不是我们数据表定义自增 这是Mp内部算法出来的一个值 其实根据不同应用场景&#xff0c;应该使…

抖音SEO源代码的部署与搭建技巧详解

抖音SEO源代码的部署与搭建是一项重要的技术&#xff0c;促进了抖音的发展。在此&#xff0c;我将为大家详细介绍抖音SEO源代码的部署与搭建技巧。 首先&#xff0c;我们需要了解抖音SEO源代码的含义。SEO源代码是搜索引擎优化的核心&#xff0c;它是用于帮助搜索引擎更好地理解…

PHP使用Redis实战实录3:数据类型比较、大小限制和性能扩展

PHP使用Redis实战实录系列 PHP使用Redis实战实录1&#xff1a;宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案PHP使用Redis实战实录2&#xff1a;Redis扩展方法和PHP连接Redis的多种方案PHP使用Redis实战实录3&#xff1a;数据类型比较、大小限制和性能扩展 数据类型…

pytorch的发展历史,与其他框架的联系

我一直是这样以为的&#xff1a;pytorch的底层实现是c(这一点没有问题&#xff0c;见下边的pytorch结构图),然后这个部分顺理成章的被命名为torch,并提供c接口,我们在python中常用的是带有python接口的&#xff0c;所以被称为pytorch。昨天无意中看到Torch是由lua语言写的&…

docker 部署 mysql8.0 无法访问

文章目录 &#x1f5fd;先来说我的是什么情况&#x1fa81;问题描述&#x1fa81;解决方法&#xff1a;✔️1 重启iptables✔️2 重启docker &#x1fa81;其他有可能连不上的原因✔️1 客户端不支持caching_sha2_password的加密方式✔️2 my.conf 配置只有本机可以访问 &#…