【序列召回推荐】(task5)多兴趣召回Comirec-DR

news2025/1/16 21:44:18

note:

  • 多兴趣召回建模。Comirec论文中的提出的第一个模型:Comirec-DR(DR就是dynamic routing),阿里将用户行为序列的item embeddings作为初始的capsule,然后提取出多个兴趣capsules,即为用户的多个兴趣。其中胶囊网络中的动态路由算法是原始的dynamic routing(CapsNet),和MIND动态路由的两大不同点:
    • 输入序列胶囊 i i i与所产生的兴趣胶囊 j j j 的权重 b i j b_{i j} bij初始化为0;
    • 在Comirec-DR中对于不同的序列胶囊i与兴趣胶囊j,我们都有一个独立的 W i j ∈ R d × d W_{i j} \in \mathbb{R}^{d \times d} WijRd×d来完成序列胶囊i到兴趣胶囊j之间的映射
  • 原论文是按照8:1:1划分训练集、验证集、测试集(按照用户划分,更具泛化能力)。
    • 训练样本:全部点击序列 ( e 1 ( u ) , e 2 ( u ) , … , e k ( u ) , … , e n ( u ) ) \left(e_1^{(u)}, e_2^{(u)}, \ldots, e_k^{(u)}, \ldots, e_n^{(u)}\right) (e1(u),e2(u),,ek(u),,en(u)),用前(n-1)个item预测第n个;
    • 验证集合测试集则是使用用户前80%的点击序列作为模型输入,然后预测后面的点击序列。

文章目录

  • note:
  • 一、Comirec模型概览
    • 1.1 Notation
    • 1.2 模型框架
    • 1.3 model training
  • 二、Comirec-DR模型
    • 2.1 数据集划分
    • 2.2 Comirec-DR中的动态路由
    • 2.3 Comirec-DR与MIND的异同
  • 三、代码实践
    • 3.1 Capsule network定义
    • 3.2 Comirec-DR类定义
    • 3.3 基于Faiss的向量召回
  • 时间安排
  • Reference

一、Comirec模型概览

在这里插入图片描述
Comirec:Controllable Multi-Interest Framework for Recommendation
论文链接:https://arxiv.org/abs/2005.09347
在这里插入图片描述
Comirec是阿里发表在KDD 2020上的一篇工作,这篇论文对MIND多行为召回进行了扩展:

  • 一方面改进了MIND中的动态路由算法,
  • 另一方面提出了一种新的多兴趣召回方法,同时对推荐的多样性层面也做出了一定的贡献,通过一种贪心的做法,在损失较小的推荐效果的情况下可以显著的提高推荐的多样性(感觉非常非常牵强,我们这里就不介绍这个了),从而极大的提高用户的使用体验,可以更进一步的探索用户的潜在兴趣。

1.1 Notation

假设一个用户集合 u ∈ U u \in \mathcal{U} uU 和一个物品集合 i ∈ I i \in \mathcal{I} iI, 对于每一个用户, 定义用户序列 ( e 1 ( u ) , e 2 ( u ) , … , e n ( u ) ) \left(e_1^{(u)}, e_2^{(u)}, \ldots, e_n^{(u)}\right) (e1(u),e2(u),,en(u)), 根据时间先后顺序排序,其中 e t ( u ) e_t^{(u)} et(u) 记录了第 t t t 个物品与用户交互。

在这里插入图片描述

1.2 模型框架

在这里插入图片描述
Comirec流程和MIND类似(区别更大是后面task6的Comirec-SR):

  • 用户历史交互item ids经过embedding layer转为item embeddings;
  • 把刚才的item embedding经过多兴趣抽取层,得到K个interest embeddings;
  • training:和之前MIND说的做法一样(取内积最大值),即选出与target item embedding最接近的interest embeddings,基于多分类任务,使用负采样sampled softmax loss;
  • serving(predict):对于每个用户的K个interest embedding进行top-N检索,即召回K X N个item。
  • aggregation module:将K X N个item送入该模块得到N个item(选内积分数最高的N个,和MIND做法一致): f ( u , i ) = max ⁡ 1 ≤ k ≤ K ( e i ⊤ v u ( k ) ) f(u, i)=\max _{1 \leq k \leq K}\left(\mathbf{e}_i^{\top} \mathbf{v}_u^{(k)}\right) f(u,i)=1kKmax(eivu(k))

1.3 model training

和1.1说的一样,某用户经过多兴趣提取层后得到K个interest embeddings后,从中找出和target item i(即下面embedding为 e i e_i ei)最接近,即内积最大的一个interest embeeding,方法如下 v u = V u [ : , argmax ⁡ ( V u ⊤ e i ) ] , \mathbf{v}_u=\mathbf{V}_u\left[:, \operatorname{argmax}\left(\mathbf{V}_u^{\top} \mathbf{e}_i\right)\right], vu=Vu[:,argmax(Vuei)],
和之前一样是结合负采样的最大似然法(后面代码用少量数据,直接交叉熵损失函数)。给出一个训练样本 ( u , i ) (u, i) (u,i), 用户embedding v u \mathbf{v}_u vu, 和物品embedding e i \mathbf{e}_i ei,则用户与物品交互的似然函数:
P θ ( i ∣ u ) = exp ⁡ ( v u ⊤ e i ) ∑ k ∈ I exp ⁡ ( v u ⊤ e k ) P_\theta(i \mid u)=\frac{\exp \left(\mathbf{v}_u^{\top} \mathbf{e}_i\right)}{\sum_{k \in I} \exp \left(\mathbf{v}_u^{\top} \mathbf{e}_k\right)} Pθ(iu)=kIexp(vuek)exp(vuei)
目标为最小化该似然函数:
 loss  = ∑ u ∈ U ∑ i ∈ I u − log ⁡ P θ ( i ∣ u ) \text { loss }=\sum_{u \in \mathcal{U}} \sum_{i \in I_u}-\log P_\theta(i \mid u)  loss =uUiIulogPθ(iu)

二、Comirec-DR模型

2.1 数据集划分

原论文是按照8:1:1划分训练集、验证集、测试集(按照用户划分,更具泛化能力)。

  • 训练样本:全部点击序列 ( e 1 ( u ) , e 2 ( u ) , … , e k ( u ) , … , e n ( u ) ) \left(e_1^{(u)}, e_2^{(u)}, \ldots, e_k^{(u)}, \ldots, e_n^{(u)}\right) (e1(u),e2(u),,ek(u),,en(u)),用前(n-1)个item预测第n个;
  • 验证集合测试集则是使用用户前80%的点击序列作为模型输入,然后预测后面的点击序列。

2.2 Comirec-DR中的动态路由

  • primary capsules:用户行为序列的物品embedding
  • interest capsules:多元用户兴趣

在这里插入图片描述
Comirec-DR和MIND一样都是使用胶囊网络中的Dynamic Routing算法进行多兴趣embedding的提取:

  • 1.第一行对输入序列胶囊 i i i 与所产生的兴趣胶囊 j j j 的权重 b i j b_{i j} bij 初始化为 0
  • 2.第二行开始进行三次动态路由
  • 3.第三行是对每一个序列胶囊i对应的所有兴趣胶囊j的权重 { b i j , j = 1 , … , K } \left\{b_{i j}, j=1, \ldots, K\right\} {bij,j=1,,K}进行Softmax归一化
  • 4.第四行是对每一个兴趣胶囊j对应所有的序列胶囊i执行第四行中的计算
    • 注意: W i j ∈ R d × d W_{i j} \in \mathbb{R}^{d \times d} WijRd×d为序列胶囊i到兴趣胶囊j的映射矩阵(即转换矩阵,MIND中是使用同一个矩阵),这样就完成了对序列到单个兴趣胶囊的特征提取,以此类推我们可以得到所有的兴趣胶囊
    • c i j c_{i j} cij 为「耦合系数」(coupling coefficients), 由迭代动态路由过程确定
  • 5.对4中得到的兴趣胶囊的表征通过squash激活函数激活
    squash ⁡ ( z j ) = ∥ z j ∥ 2 1 + ∥ z j ∥ 2 z j ∥ z j ∥ \operatorname{squash}\left(z_j\right)=\frac{\left\|z_j\right\|^2}{1+\left\|z_j\right\|^2} \frac{z_j}{\left\|z_j\right\|} squash(zj)=1+zj2zj2zjzj
  • 6.最后我们通过第6行中的公式来更新 b i j b_{i j} bij(注意不是像NN一样通过反向传播更新参数)
  • 7.至此就完成了一次动态路由, 我们将这个过程重复三次就得到了完整的动态路由, 也就完成了多兴趣表征的建模

2.3 Comirec-DR与MIND的异同

在这里插入图片描述
Comirec-DR与MIND的核心区别主要有两个:

  • 1.输入序列胶囊 i i i与所产生的兴趣胶囊 j j j 的权重 b i j b_{i j} bij 的初始化方式不一样:
    • 在Comirec-DR中对 b i j b_{i j} bij 全部初始化为 0 ,
    • 在MIND中对 b i j b_{i j} bij 全部用高斯分布分布进行初始化的映射矩阵使用同一矩阵 S ∈ R d × d S \in \mathbb{R}^{d \times d} SRd×d
  • 2.在进行序列胶囊与兴趣胶囊之间的映射转换时的变量声明方式不一样:
    • 在Comirec-DR中对于不同的序列胶囊i与兴趣胶囊j,我们都有一个独立的 W i j ∈ R d × d W_{i j} \in \mathbb{R}^{d \times d} WijRd×d来完成序列胶囊i到兴趣胶囊j之间的映射
    • 在MIND中,其提出的B2I Dynamic Routing中将所有的序列胶囊i与兴趣胶囊j的映射矩阵使用同一矩阵 S ∈ R d × d S \in \mathbb{R}^{d \times d} SRd×d

三、代码实践

3.1 Capsule network定义

为了完成了对序列到单个兴趣胶囊的特征提取,将 W i j ∈ R d × d W_{i j} \in \mathbb{R}^{d \times d} WijRd×d作为序列胶囊i到兴趣胶囊j的映射矩阵(MIND中是使用同一个矩阵),即可学习的参数。以此类推我们可以得到所有的兴趣胶囊。所以下面改成self.create_parameter,如果可以用torch是用torch.nn.Parameter函数,可以理解为类型转化函数,将一个不可训练类型Tensor转为可以训练的parameter。

class CapsuleNetwork(nn.Layer):

    def __init__(self, hidden_size, seq_len, bilinear_type=2, interest_num=4, routing_times=3, hard_readout=True,
                 relu_layer=False):
        super(CapsuleNetwork, self).__init__()
        self.hidden_size = hidden_size  # h
        self.seq_len = seq_len  # s
        self.bilinear_type = bilinear_type
        self.interest_num = interest_num
        self.routing_times = routing_times
        self.hard_readout = hard_readout
        self.relu_layer = relu_layer
        self.stop_grad = True
        self.relu = nn.Sequential(
            nn.Linear(self.hidden_size, self.hidden_size, bias_attr=False),
            nn.ReLU()
        )
        if self.bilinear_type == 0:  # MIND
            self.linear = nn.Linear(self.hidden_size, self.hidden_size, bias_attr=False)
        elif self.bilinear_type == 1:
            self.linear = nn.Linear(self.hidden_size, self.hidden_size * self.interest_num, bias_attr=False)
        else:  # ComiRec_DR
            self.w = self.create_parameter(
                shape=[1, self.seq_len, self.interest_num * self.hidden_size, self.hidden_size])

    def forward(self, item_eb, mask):
        if self.bilinear_type == 0:  # MIND
            item_eb_hat = self.linear(item_eb)  # [b, s, h]
            item_eb_hat = paddle.repeat_interleave(item_eb_hat, self.interest_num, 2) # [b, s, h*in]
        elif self.bilinear_type == 1:
            item_eb_hat = self.linear(item_eb)
        else:  # ComiRec_DR
            u = paddle.unsqueeze(item_eb, 2)  # shape=(batch_size, maxlen, 1, embedding_dim)
            item_eb_hat = paddle.sum(self.w[:, :self.seq_len, :, :] * u,
                                    3)  # shape=(batch_size, maxlen, hidden_size*interest_num)

        item_eb_hat = paddle.reshape(item_eb_hat, (-1, self.seq_len, self.interest_num, self.hidden_size))
        item_eb_hat = paddle.transpose(item_eb_hat, perm=[0,2,1,3])
        # item_eb_hat = paddle.reshape(item_eb_hat, (-1, self.interest_num, self.seq_len, self.hidden_size))

        # [b, in, s, h]
        if self.stop_grad:  # 截断反向传播,item_emb_hat不计入梯度计算中
            item_eb_hat_iter = item_eb_hat.detach()
        else:
            item_eb_hat_iter = item_eb_hat

        # b的shape=(b, in, s)
        if self.bilinear_type > 0:  # b初始化为0(一般的胶囊网络算法)
            capsule_weight = paddle.zeros((item_eb_hat.shape[0], self.interest_num, self.seq_len))
        else:  # MIND使用高斯分布随机初始化b
            capsule_weight = paddle.randn((item_eb_hat.shape[0], self.interest_num, self.seq_len))

        for i in range(self.routing_times):  # 动态路由传播3次
            atten_mask = paddle.repeat_interleave(paddle.unsqueeze(mask, 1), self.interest_num, 1) # [b, in, s]
            paddings = paddle.zeros_like(atten_mask)

            # 计算c,进行mask,最后shape=[b, in, 1, s]
            capsule_softmax_weight = F.softmax(capsule_weight, axis=-1)
            capsule_softmax_weight = paddle.where(atten_mask==0, paddings, capsule_softmax_weight)  # mask
            capsule_softmax_weight = paddle.unsqueeze(capsule_softmax_weight, 2)

            if i < 2:
                # s=c*u_hat , (batch_size, interest_num, 1, seq_len) * (batch_size, interest_num, seq_len, hidden_size)
                interest_capsule = paddle.matmul(capsule_softmax_weight,
                                                item_eb_hat_iter)  # shape=(batch_size, interest_num, 1, hidden_size)
                cap_norm = paddle.sum(paddle.square(interest_capsule), -1, keepdim=True)  # shape=(batch_size, interest_num, 1, 1)
                scalar_factor = cap_norm / (1 + cap_norm) / paddle.sqrt(cap_norm + 1e-9)  # shape同上
                interest_capsule = scalar_factor * interest_capsule  # squash(s)->v,shape=(batch_size, interest_num, 1, hidden_size)

                # 更新b
                delta_weight = paddle.matmul(item_eb_hat_iter,  # shape=(batch_size, interest_num, seq_len, hidden_size)
                                            paddle.transpose(interest_capsule, perm=[0,1,3,2])
                                            # shape=(batch_size, interest_num, hidden_size, 1)
                                            )  # u_hat*v, shape=(batch_size, interest_num, seq_len, 1)
                delta_weight = paddle.reshape(delta_weight, (
                -1, self.interest_num, self.seq_len))  # shape=(batch_size, interest_num, seq_len)
                capsule_weight = capsule_weight + delta_weight  # 更新b
            else:
                interest_capsule = paddle.matmul(capsule_softmax_weight, item_eb_hat)
                cap_norm = paddle.sum(paddle.square(interest_capsule), -1, keepdim=True)
                scalar_factor = cap_norm / (1 + cap_norm) / paddle.sqrt(cap_norm + 1e-9)
                interest_capsule = scalar_factor * interest_capsule

        interest_capsule = paddle.reshape(interest_capsule, (-1, self.interest_num, self.hidden_size))

        if self.relu_layer:  # MIND模型使用book数据库时,使用relu_layer
            interest_capsule = self.relu(interest_capsule)

        return interest_capsule

3.2 Comirec-DR类定义

下面其实和MIND一样的,改动的都在CapsuleNetwork体现出来的。

class ComirecDR(nn.Layer):
    def __init__(self, config):
        super(ComirecDR, self).__init__()

        self.config = config
        self.embedding_dim = self.config['embedding_dim']
        self.max_length = self.config['max_length']
        self.n_items = self.config['n_items']

        self.item_emb = nn.Embedding(self.n_items, self.embedding_dim, padding_idx=0)
        self.capsule = CapsuleNetwork(self.embedding_dim, self.max_length, bilinear_type=2,
                                      interest_num=self.config['K'])
        self.loss_fun = nn.CrossEntropyLoss()
        self.reset_parameters()

    def calculate_loss(self,user_emb,pos_item):
        all_items = self.item_emb.weight
        scores = paddle.matmul(user_emb, all_items.transpose([1, 0]))
        return self.loss_fun(scores,pos_item)

    def output_items(self):
        return self.item_emb.weight

    def reset_parameters(self, initializer=None):
        for weight in self.parameters():
            paddle.nn.initializer.KaimingNormal(weight)

    def forward(self, item_seq, mask, item, train=True):

        if train:
            seq_emb = self.item_emb(item_seq)  # Batch,Seq,Emb
            item_e = self.item_emb(item).squeeze(1)

            multi_interest_emb = self.capsule(seq_emb, mask)  # Batch,K,Emb

            cos_res = paddle.bmm(multi_interest_emb, item_e.squeeze(1).unsqueeze(-1))
            k_index = paddle.argmax(cos_res, axis=1)

            best_interest_emb = paddle.rand((multi_interest_emb.shape[0], multi_interest_emb.shape[2]))
            for k in range(multi_interest_emb.shape[0]):
                best_interest_emb[k, :] = multi_interest_emb[k, k_index[k], :]

            loss = self.calculate_loss(best_interest_emb,item)
            output_dict = {
                'user_emb': multi_interest_emb,
                'loss': loss,
            }
        else:
            seq_emb = self.item_emb(item_seq)  # Batch,Seq,Emb
            multi_interest_emb = self.capsule(seq_emb, mask)  # Batch,K,Emb
            output_dict = {
                'user_emb': multi_interest_emb,
            }
        return output_dict

3.3 基于Faiss的向量召回

这里的最近邻就和之前task有点不同,因为是多兴趣评估,要num_interest个兴趣向量的所有topN近邻物品(num_interest*topN个物品)集合起来按照距离重新排序。

def get_predict(model, test_data, hidden_size, topN=20):

    item_embs = model.output_items().cpu().detach().numpy()
    item_embs = normalize(item_embs, norm='l2')
    gpu_index = faiss.IndexFlatIP(hidden_size)
    gpu_index.add(item_embs)
    
    test_gd = dict()
    preds = dict()
    
    user_id = 0

    for (item_seq, mask, targets) in tqdm(test_data):

        # 获取用户嵌入
        # 多兴趣模型,shape=(batch_size, num_interest, embedding_dim)
        # 其他模型,shape=(batch_size, embedding_dim)
        user_embs = model(item_seq,mask,None,train=False)['user_emb']
        user_embs = user_embs.cpu().detach().numpy()

        # 用内积来近邻搜索,实际是内积的值越大,向量越近(越相似)
        if len(user_embs.shape) == 2:  # 非多兴趣模型评估
            user_embs = normalize(user_embs, norm='l2').astype('float32')
            D, I = gpu_index.search(user_embs, topN)  # Inner Product近邻搜索,D为distance,I是index
#             D,I = faiss.knn(user_embs, item_embs, topN,metric=faiss.METRIC_INNER_PRODUCT)
            for i, iid_list in enumerate(targets):  # 每个用户的label列表,此处item_id为一个二维list,验证和测试是多label的
                test_gd[user_id] = iid_list
                preds[user_id] = I[i,:]
                user_id +=1
        else:  # 多兴趣模型评估
            ni = user_embs.shape[1]  # num_interest
            user_embs = np.reshape(user_embs,
                                   [-1, user_embs.shape[-1]])  # shape=(batch_size*num_interest, embedding_dim)
            user_embs = normalize(user_embs, norm='l2').astype('float32')
            D, I = gpu_index.search(user_embs, topN)  # Inner Product近邻搜索,D为distance,I是index
#             D,I = faiss.knn(user_embs, item_embs, topN,metric=faiss.METRIC_INNER_PRODUCT)
            for i, iid_list in enumerate(targets):  # 每个用户的label列表,此处item_id为一个二维list,验证和测试是多label的
                recall = 0
                dcg = 0.0
                item_list_set = []

                # 将num_interest个兴趣向量的所有topN近邻物品(num_interest*topN个物品)集合起来按照距离重新排序
                item_list = list(
                    zip(np.reshape(I[i * ni:(i + 1) * ni], -1), np.reshape(D[i * ni:(i + 1) * ni], -1)))
                item_list.sort(key=lambda x: x[1], reverse=True)  # 降序排序,内积越大,向量越近
                for j in range(len(item_list)):  # 按距离由近到远遍历推荐物品列表,最后选出最近的topN个物品作为最终的推荐物品
                    if item_list[j][0] not in item_list_set and item_list[j][0] != 0:
                        item_list_set.append(item_list[j][0])
                        if len(item_list_set) >= topN:
                            break
                test_gd[user_id] = iid_list
                preds[user_id] = item_list_set
                user_id +=1
    return test_gd, preds

def evaluate(preds,test_gd, topN=50):
    total_recall = 0.0
    total_ndcg = 0.0
    total_hitrate = 0
    for user in test_gd.keys():
        recall = 0
        dcg = 0.0
        item_list = test_gd[user]
        for no, item_id in enumerate(item_list):
            if item_id in preds[user][:topN]:
                recall += 1
                dcg += 1.0 / math.log(no+2, 2)
            idcg = 0.0
            for no in range(recall):
                idcg += 1.0 / math.log(no+2, 2)
        total_recall += recall * 1.0 / len(item_list)
        if recall > 0:
            total_ndcg += dcg / idcg
            total_hitrate += 1
    total = len(test_gd)
    recall = total_recall / total
    ndcg = total_ndcg / total
    hitrate = total_hitrate * 1.0 / total
    return {f'recall@{topN}': recall, f'ndcg@{topN}': ndcg, f'hitrate@{topN}': hitrate}

# 指标计算
def evaluate_model(model, test_loader, embedding_dim,topN=20):
    test_gd, preds = get_predict(model, test_loader, embedding_dim, topN=topN)
    return evaluate(preds, test_gd, topN=topN)

时间安排

任务信息截止时间完成情况
11月14日周一正式开始
Task01:Paddle开发深度学习模型快速入门11月14、15、16日周三完成
Task02:传统序列召回实践:GRU4Rec11月17、18、19日周六完成
Task03:GNN在召回中的应用:SR-GNN11月20、21、22日周二完成
Task04:多兴趣召回实践:MIND11月23、24、25、26日周六完成
Task05:多兴趣召回实践:Comirec-DR11月27、28日周一完成
Task06:多兴趣召回实践:Comirec-SA11月29日周二

Reference

[1] 多兴趣召回实践:Comirec-DR
论文:Controllable Multi-Interest Framework for Recommendation
链接:https://arxiv.org/abs/2005.09347
tensorflow代码1;Pytorch代码2
[2] 多兴趣召回实践:Comirec-SA
论文:Controllable Multi-Interest Framework for Recommendation
链接:https://arxiv.org/abs/2005.09347
代码1:https://github.com/THUDM/ComiRec/blob/a576eed8b605a531f2971136ce6ae87739d47693/src/model.py
代码2:https://github.com/ShiningCosmos/pytorch_ComiRec/blob/main/ComiRec.py
[3] 推荐场景中召回模型的演化过程. 京东大佬
[4] 推荐系统论文阅读(四十三)-Comirec:阿里又一篇多兴趣召回的论文
[5] 多兴趣推荐召回模型:ComiRec
[6] ComiRec-DR和MIND中Dynamic Routing的差异
[7] KDD2020|阿里团队最新的多元兴趣推荐模型—ComiRec
[8] Yukuo Cen, Xu Zou, Jianwei Zhang, Hongxia Yang, Jingren Zhou, and Jie Tang.2019. Representation learning for attributed multiplex heterogeneous network. In KDD’19. 1358–1368.
[9] Sara Sabour, Nicholas Frosst, and Geoffrey E Hinton. 2017. Dynamic routing between capsules. In NIPS’17. 3856–3866.

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

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

相关文章

【Java】博客系统——详细解释+代码+详细注释(课设必过)

目录 前言 博客系统简要分析 一、数据库的设计 1.1 分析 1.2 代码实现&#xff08;创建数据库和表&#xff09; 二、封装数据库&#xff08;JDBC代码的编写&#xff09; 2.1、首先通过创建Maven项目&#xff0c;基于Small Tomcat部署 servlet&#xff1b; 2.2、封装数据…

telnet配置设备远程管理—eNSP

案例&#xff1a;给路由器配置远程管理&#xff0c;使一台路由器远程管理另一台。 所需设备&#xff1a;两台路由器&#xff0c;一根网线 图示 一、给两台设备配置IP地址 AR1&#xff08;以下命令&#xff09; a. sy b. int g0/0/0 c. ip add 1.1.1.1 24AR2 a. sy b. int g0/0…

区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作

区间信息维护与查询【线段树 】 - 原理1 线段树的基本操作 线段树&#xff08;segment tree&#xff09;是一种基于分治思想的二叉树&#xff0c;它的每个节点都对应一个[L , R ]区间&#xff0c;叶子节点对应的区间L R 。每一个非叶子节点[L , R ]其左子节点的区间都为[L , (…

进程与线程的区别及联系

目录 1. 操作系统功能简介 2. 进程 2.1 认识进程 2.2 进程操作系统中如何管理 2.3 PCB如何描述 2.3.1 pid 2.3.2 内存指针 2.3.3 文件描述符表 2.3.4 进程调度相关属性 3. 内存管理 4. 线程 4.1 认识线程 4.2 进程与线程的关系 4.3 线程安全问题 1.操作系统功能简…

[附源码]计算机毕业设计springboot电子相册管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Git 入门 拉取仓库和推送仓库

目录 基本操作 本地建立仓库并推送到远端仓库 关联仓库失败 解决方法 从远端仓库拉取文件到本地 私有的仓库的连接 修改 提交者名称 IDEA/Pycharm等如何使用git 如何关闭git 功能 Git操作主要分为两类 &#xff0c;如何把自己建的项目同步的网上的仓库&#xff0c;如何…

OpenHarmony编译系统

GN 简介 直接百度 GN 入门 可以参考下面的示例&#xff0c;作为入门参考学习https://blog.csdn.net/weixin_44701535/article/details/88355958https://gn.googlesource.com/gn//main/docs/reference.mdhttps://chromium.googlesource.com/chromium/src/tools/gn//48062805e…

Java项目_在线点餐系统(jsp+sevlet+mysql)(含论文)

在线点餐系统(jspsevletmysql一、系统介绍二、功能展示1.主页(用户)2.菜单(用户)3.用户注册(用户)4.用户登陆(用户)5.我的订单(用户)6.餐桌管理(管理员)7.菜系管理(管理员)8.菜品管理(管理员)9.订单管理(管理员)三、获取源码一、系统介绍 系统主要功能&#xff1a; 用户&#…

4位资深专家多年大厂经验分享出Flink技术架构设计与实现原理

时间飞逝&#xff0c;转眼间毕业七年多&#xff0c;从事 Java 开发也六年了。我在想&#xff0c;也是时候将自己的 Java 整理成一套体系。 这一次的知识体系面试题涉及到 Java 知识部分、性能优化、微服务、并发编程、开源框架、分布式等多个方面的知识点。 写这一套 Java 面试…

bootstrap下拉菜单学习(五)

组件&#xff1a;下拉菜单 bootstrap字体图标和下拉菜单组件的使用 这些图标都存在我们引入的font文件夹内&#xff1a; 复制bootstrap所用的包&#xff1a; 创建html页面&#xff1a; 图标不仅可以直接放文本里面&#xff0c; 还可有结合按钮去用。 要使用组件&#xff1a;不…

《网络空间测绘技术与实践》正式发售,让网络空间作战“有图可依”

近日&#xff0c;多位业界专家力推&#xff0c;由知道创宇CEO赵伟、CTO杨冀龙、CSO黑哥&#xff08;周景平&#xff09;等撰写的著作《网络空间测绘技术与实践》&#xff0c;正式出版并发售。网络空间已成为继“陆、 海、空、天”后的第五大空间&#xff0c;网络空间亦需要类似…

【uni-app高频面试题——精品一】

uni-app高频面试题谈谈你对uni-app的理解&#x1f355;uni中如何为不同的平台设置不同的代码uniapp中封装接口请求相较于微信小程序有什么要注意的uni-app中的本地存储数据和接收数据是什么✊uni-app 路由与页面跳转&#x1f4aa;uni-app全局变量怎么定义&#xff0c;怎么获取&…

【Python实战】“特种兵”们的专属游戏助手,助你吃鸡:极品小助手也是棒呆了~(“大吉大利,今W吃鸡”)

前言 有温度 有深度 有广度 就等你来关注哦~ 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 “注意左边&#xff0c;左边有人&#xff0c;打他&#xff01;” “快上车&#xff01;&#xff0…

Spring Security(十九)--OAuth2:实现授权服务器(下)--环境准备以及骨架代码搭建

一、前言 本章我们将在上一章代码骨架搭建好的前提下对三种授权类型进行测试以及讲解如何配置授权服务器以颁发刷新令牌&#xff0c;所以本章是一个比较轻松的章节&#xff0c;但是唯一的要求就是需要小伙伴们对上一章内容要完成代码的搭建&#xff0c;否则这章学习也不知道个…

[附源码]计算机毕业设计springboot飞越青少儿兴趣培训机构管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

嘉创房地产冲刺港交所:半年营收4.7亿 现金及现金等价物减少

雷递网 雷建平 11月28日嘉创房地产控股有限公司&#xff08;简称&#xff1a;“嘉创”&#xff09;日前递交招股书&#xff0c;准备在港交所上市。半年营收4.73亿嘉创为一家精品住宅物业发展商&#xff0c;主要在大湾区的东莞、惠州及佛山迅速发展的住宅市场&#xff08;如东莞…

m半分布式JAC联合接纳控制与用户位置信息的垂直切换matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 随着无线通信技术的飞速发展&#xff0c;为支持多种不同无线接入技术、不同系统间协作、不同业务类型及终端差异性等需求&#xff0c;未来的无线网络将是一种协作式的异构网络融合架构&#xff0…

文理导航杂志文理导航杂志社文理导航编辑部2022年第12期目录

专题研究《文理导航》投稿&#xff1a;cn7kantougao163.com 初中科学生活化作业优化策略 唐黎娜; 4-6 揭密2022年新高考1卷解析几何解答题 陈思伽; 7-9 初中数学错题资源的有效运用 王丹; 10-12 浅谈陶行知思想在初中体育教学中的应用 王树华; 13-15 高中数…

Error: error:0308010C:digital envelope routines::unsupported(vue2项目报错)

问题描述 在 终端输入 npm run dev 命令&#xff0c;项目运行报错 Error: error:0308010C:digital envelope routines::unsupported 问题原因 node 版本过高&#xff0c;可以在命令行 输入 node -v 查看版本 因为 Node.js 版本是 17 以上所以会运行失败&#xff0c; Node.j…

MySQL为自动编号的字段赋值

insert users values(NULL,ming,fasdfasdfasd,22,1); 或者 insert users values(DEFAULT,ming,fasdfasdfasd,22,1);