基于Node2Vec的图嵌入实现过程

目录

  • 一、引言
  • 二、Node2Vec(原理)
    • 2.1 随机游走(Random Walk)
    • 2.2 嵌入学习
    • 2.3 Node2Vec 的优势
  • 三、使用 Node2Vec 进行图嵌入(实践)
    • 3.1 读取和转换 JSON 文件为 Graph 对象
    • 3.2 训练 Node2Vec 模型
    • 3.3 二维嵌入可视化
    • 3.4 三维嵌入可视化
    • 3.5 保存嵌入和关系到 JSON 文件
    • 3.6 完整代码
  • 四、结论

一、引言

  随着互联网的发展,社交网络、推荐系统以及知识图谱等应用中的图结构数据越来越多,如何从这些复杂的关系网中提取出有意义的信息,成为了机器学习中的一大挑战。图结构数据的关键在于节点(如用户、物品等)之间的关联关系,而要从图中有效提取节点的特征,我们需要一种能保留图的结构信息的嵌入方法。

  传统的机器学习方法难以直接处理图数据,因为图的结构不如图像和文本那样规则,图中的节点没有固定的顺序,也没有明确的拓扑形式。因此,近年来图嵌入(Graph Embedding)技术得到了广泛关注。通过图嵌入方法,我们可以将图中的节点映射为低维向量,使其能够被应用到分类、聚类、推荐等任务中。

  Node2Vec 是一种基于随机游走(Random Walk)的节点嵌入方法。它通过对图中的节点进行采样,并利用这些采样结果训练出能够表示节点的嵌入向量。这些向量不仅能保留节点的局部邻域信息,还能反映节点在整个图中的全局位置。本文的目的在于详细介绍如何通过代码实践使用Node2Vec进行图嵌入、对嵌入结果进行可视化,并最终将嵌入向量存储为JSON文件。

二、Node2Vec(原理)

2.1 随机游走(Random Walk)

  Node2Vec 基于图上的随机游走来捕捉节点的结构特性。随机游走是一种类似于在图上进行漫步的过程,即从一个节点出发,每次根据一定的概率选择下一个要访问的节点。通过多次游走,Node2Vec可以得到一系列的节点序列,这些序列可以看作是图中的“路径”,用于训练节点的嵌入表示。

  Node2Vec 提供了两个关键参数 p p p q q q,用来控制随机游走的方式:

  • 参数 p p p:回归系数,用于控制游走时返回前一个节点的概率。 p p p 越大,模型越倾向于向更远的节点扩展。

  • 参数 q q q:探索系数,用于控制模型深度优先搜索(DFS)和广度优先搜索(BFS)之间的平衡。 q q q 越大,游走更倾向于深度探索,即更多地访问图的深层节点; q q q 较小时,游走更倾向于广度探索,即优先访问节点的直接邻居。

  通过调节这两个参数,Node2Vec 能够适应不同的任务需求。例如,在社交网络中,我们可能希望重点关注与用户相邻的其他用户,此时较小的 q q q值能帮助我们更好地捕捉局部社区结构。而在知识图谱中,我们可能希望深入挖掘某个概念与远端概念的关系,此时较大的 q q q 值可以帮助捕获图的全局结构。

在这里插入图片描述

2.2 嵌入学习

  一旦通过随机游走得到节点序列,Node2Vec 将这些序列视为词语序列,采用类似 Word2Vec 的 Skip-Gram 模型进行嵌入学习。Skip-Gram 模型的目标是给定一个节点,预测其上下文节点(即在随机游走序列中与该节点相邻的节点)。

  在这个过程中,Node2Vec 学习到每个节点的嵌入向量。这些嵌入向量能够保留节点之间的相似性,邻近的节点将拥有相近的嵌入表示。最终,通过这种方式,图中的每个节点都被映射到一个固定维度的向量空间中,嵌入向量可以直接用于下游任务,如节点分类、边预测和社区检测。

2.3 Node2Vec 的优势

  Node2Vec 的最大优势在于它可以同时捕捉局部和全局的图结构信息。相比于传统的图算法(如PageRank)只考虑节点的全局属性,或者某些仅仅考虑局部连接的嵌入方法,Node2Vec 能够通过灵活的随机游走策略,在不同的场景下取得更好的表现。

  此外,Node2Vec 的随机游走策略可以高效处理大型图,尤其是对于节点和边非常多的复杂网络,Node2Vec 仍然能通过较少的计算时间获得高质量的嵌入向量。

三、使用 Node2Vec 进行图嵌入(实践)

3.1 读取和转换 JSON 文件为 Graph 对象

  在正式训练模型之前,首先我们需要将原始的数据加载为图结构。在很多实际应用场景中,图结构的输入数据并不是直接以图的形式给出,而是以诸如JSON等格式的文件存储了节点之间的关系。因此,第一步的任务是将JSON文件中的节点和边转化为可以用于训练的 Graph 对象。(本文使用的json文件可以从百度网盘下载)

import json
import networkx as nx# 读取并转换JSON文件为Graph对象的函数
def read_and_convert_json_to_graph(json_file):with open(json_file, 'r') as file:json_list = json.load(file)G = nx.Graph()  # 创建一个空的无向图for item in json_list:G.add_edge(item['object1'], item['object2'])  # 添加边return G

  在上面的代码中,read_and_convert_json_to_graph 函数负责读取JSON文件,并将其中的节点(object1 和 object2)之间的关系转换为图的边(edge)。具体过程如下:

  • 通过 json.load() 将JSON文件中的内容加载为Python的列表格式。

  • 遍历该列表中的每个元素,并使用 networkx 库将两个节点之间的关系加入到无向图 G 中。

  最终,我们会得到一个完整的 Graph 对象,它包含了所有节点及它们之间的连接。这个 Graph 对象将作为Node2Vec 模型的输入,进一步进行节点嵌入训练。

3.2 训练 Node2Vec 模型

  接下来,利用构建好的图对象,我们将使用Node2Vec库进行节点嵌入的训练。Node2Vec 的核心思想是通过随机游走生成节点序列,并通过这些序列学习节点的嵌入表示。

from node2vec import Node2Vec# Node2Vec模型训练函数
def train_node2vec(graph, dimensions=64, walk_length=30, num_walks=200, p=1, q=1, workers=4):node2vec = Node2Vec(graph, dimensions=dimensions, walk_length=walk_length, num_walks=num_walks, p=p, q=q, workers=workers)model = node2vec.fit(window=10, min_count=1, batch_words=4)return model

  在这个 train_node2vec 函数中,我们定义了几个关键参数来控制Node2Vec的训练过程:

  • dimensions:嵌入向量的维度,即我们希望每个节点被映射到多少维度的向量空间。通常,维度越高,模型能表达的信息越丰富,但训练时间也会增加。

  • walk_length:随机游走的长度,决定每次游走过程中访问的节点数目。

  • num_walks:每个节点的游走次数,即我们为每个节点采样的路径数量。

  • p 和 q:前面介绍的控制游走策略的两个参数,分别控制返回前一节点的概率和探索新节点的概率。

  • workers:并行计算的线程数,用于加速训练过程。

  训练完成后,fit() 函数返回的是一个包含了所有节点嵌入向量的模型对象。我们可以通过该模型获取每个节点的嵌入向量,并将这些向量用于后续的任务。

3.3 二维嵌入可视化

  在训练完Node2Vec模型并得到节点的高维嵌入向量后,直接查看这些高维向量并不直观,因此我们通常需要将其降维,以便通过图形展示这些节点在嵌入空间中的分布情况。

  二维可视化是最常用的手段之一。我们可以使用降维算法(如主成分分析 PCA)将嵌入向量从高维空间降至二维,从而通过平面图直观展示节点之间的相对位置。接下来是实现代码:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA# 二维可视化嵌入的函数
def visualize_embeddings(model, graph, save_path='embeddings_visualization.png'):# 获取所有节点的嵌入向量embeddings = np.array([model.wv[str(node)] for node in graph.nodes()])# 使用PCA将嵌入向量降维到2Dpca = PCA(n_components=2)embeddings_2d = pca.fit_transform(embeddings)# 绘制图形plt.figure(figsize=(20, 16))  # 设置图形大小pos = {node: embeddings_2d[i] for i, node in enumerate(graph.nodes())}# 绘制节点,设置节点大小和颜色nx.draw_networkx_nodes(graph, pos=pos, node_size=30, node_color='blue')# 绘制图中的边,透明度alpha设置较低以减少边线干扰nx.draw_networkx_edges(graph, pos=pos, alpha=0.1)# 隐藏坐标轴plt.axis('off')# 保存结果并展示图像plt.savefig(save_path)plt.show()
  • 获取节点嵌入向量:从训练好的 Node2Vec 模型中提取节点的嵌入向量,这些嵌入向量被保存在 model.wv 中,其中每个节点的 ID 被转化为字符串格式。

  • PCA降维:使用 PCA 将高维嵌入向量降至二维。PCA 是一种常见的降维技术,它能够提取数据中最重要的两个主成分,使得降维后的数据尽可能保留原始高维数据中的信息。

  • 节点和边的可视化:通过 networkx 提供的 draw_networkx_nodes 和 draw_networkx_edges 方法来绘制图形中的节点和边。节点颜色为蓝色,边的透明度较低,以减少边线的干扰,使节点的位置关系更加清晰可见。

  • 图形保存与显示:绘制好的图形保存为 PNG 文件,并显示出来以供查看。plt.savefig() 可以保存结果,plt.show() 用于在执行程序时显示图像。

  通过这种方式,节点会根据其嵌入向量的相似性在二维平面上分布。距离较近的节点往往在嵌入空间中更为相似,说明它们在图结构中有更密切的联系。反之,距离较远的节点可能属于不同的社区或子图。

3.4 三维嵌入可视化

  虽然二维可视化已经能帮助我们理解节点嵌入,但在某些场景中,我们可能希望更丰富的维度来展示节点的分布。此时,三维可视化可以提供更直观的展示效果。通过降维到三维,我们能够通过三维图形进一步观察节点的相互关系。

from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import PCA# 三维可视化嵌入的函数
def visualize_embeddings_3d(model, graph, save_path='embeddings_visualization_3d.png'):# 获取所有节点的嵌入向量embeddings = np.array([model.wv[str(node)] for node in graph.nodes()])# 使用PCA将嵌入向量降维到3Dpca = PCA(n_components=3)embeddings_3d = pca.fit_transform(embeddings)# 创建3D绘图fig = plt.figure(figsize=(20, 16))ax = fig.add_subplot(111, projection='3d')# 获取嵌入向量的三个维度(x, y, z坐标)xs = embeddings_3d[:, 0]ys = embeddings_3d[:, 1]zs = embeddings_3d[:, 2]# 绘制节点ax.scatter(xs, ys, zs, c='blue', marker='o')# 设置轴标签ax.set_xlabel('PCA1')ax.set_ylabel('PCA2')ax.set_zlabel('PCA3')# 添加标题plt.title('3D visualization of Node Embeddings')# 保存并展示图像plt.savefig(save_path)plt.show()
  • PCA降维到三维:与二维可视化类似,这里我们将嵌入向量从高维空间降至三维。这三个维度能够提供更多的嵌入信息,从而帮助我们更好地观察节点的分布情况。

  • 三维绘图:通过 mpl_toolkits.mplot3d 的 Axes3D 类进行三维图形的绘制。我们提取PCA降维后的三个维度(x、y、z坐标),并将其用于三维散点图的绘制。

  • 散点绘制:使用 scatter() 函数绘制节点,每个节点显示为一个三维点。我们可以通过颜色、大小、标记等属性来调整节点的视觉效果。在本例中,节点颜色为蓝色,标记为圆形。

  • 图形轴标签与标题:为三维图形的各个坐标轴(PCA1、PCA2、PCA3)添加标签,以便清晰显示每个维度代表的含义。此外,还为图形添加了标题,方便理解可视化内容。

  三维图形相比于二维图形能够展示更多的信息。特别是在处理嵌入维度较高的情况下,三维可视化能够帮助我们更好地理解节点在嵌入空间中的位置关系。通过旋转三维图形,我们可以从不同角度观察节点的分布情况,发现潜在的社区结构或其他有趣的模式。

3.5 保存嵌入和关系到 JSON 文件

  在获得了节点嵌入向量后,我们往往需要将这些向量保存下来,方便后续的分析与应用。在实际项目中,我们可能需要将嵌入向量与原始的关系数据一起存储,以便后续模型或任务能够继续利用这些嵌入。

  下面的代码展示了如何将节点的嵌入向量与原始的JSON文件结合,并将结果保存为一个新的JSON文件。

def save_embeddings_to_json(graph, model, json_file_path, output_file):# 读取原始的JSON文件来获取对象间的关系with open(json_file_path, 'r') as file:json_list = json.load(file)output_data = []for item in json_list:object1, object2 = item['object1'], item['object2']# 对于每对对象,获取其嵌入向量properties1 = model.wv[object1].tolist() if object1 in model.wv else Noneproperties2 = model.wv[object2].tolist() if object2 in model.wv else None# 构建新的字典项,包含嵌入向量和原始关系modified_item = {"object1": object1,"properties1": properties1,"object2": object2,"properties2": properties2,"relationship": item.get('relationship')  # 假设原始JSON中包含关系字段}output_data.append(modified_item)# 将修改后的数据写入新的JSON文件with open(output_file, 'w') as f:json.dump(output_data, f, indent=4)
  • 原始关系读取:首先,我们读取原始的JSON文件,获取其中包含的节点及其关系。在本例中,JSON文件中的每一条记录包含两个对象(object1 和 object2),代表它们之间的某种关系。

  • 嵌入向量提取:对于每个对象,我们通过模型中的 model.wv 获取其对应的嵌入向量。如果某个对象没有嵌入向量(例如由于它在训练中未出现),我们可以将其设置为 None。

  • 数据结构修改:将每对对象的嵌入向量(properties1 和 properties2)与它们的关系信息(relationship)一起构造一个新的数据项。

  • 结果保存:最终,将所有修改后的数据写入一个新的JSON文件,以便在后续的分析中使用。

3.6 完整代码

import json
import numpy as np
from node2vec import Node2Vec
import networkx as nx
import matplotlib.pyplot as plt# 读取并转换JSON文件为Graph对象的函数
def read_and_convert_json_to_graph(json_file):with open(json_file, 'r') as file:json_list = json.load(file)G = nx.Graph()  # 创建一个空的无向图for item in json_list:G.add_edge(item['object1'], item['object2'])  # 添加边return G# Node2Vec模型训练函数
def train_node2vec(graph, dimensions=64, walk_length=30, num_walks=200, p=1, q=1, workers=4):# 使用Node2Vec库node2vec = Node2Vec(graph, dimensions=dimensions, walk_length=walk_length, num_walks=num_walks, p=p, q=q,workers=workers)# 训练模型model = node2vec.fit(window=10, min_count=1, batch_words=4)return model# 可视化嵌入的函数,实现二维可视化
def visualize_embeddings(model, graph, save_path='embeddings_visualization.png'):# 获取嵌入向量embeddings = np.array([model.wv[str(node)] for node in graph.nodes()])# 使用PCA进行降维以便于可视化from sklearn.decomposition import PCApca = PCA(n_components=2)embeddings_2d = pca.fit_transform(embeddings)# 绘制节点嵌入plt.figure(figsize=(20, 16))pos = {node: embeddings_2d[i] for i, node in enumerate(graph.nodes())}# 调整节点大小和边的透明度nx.draw_networkx_nodes(graph, pos=pos, node_size=30, node_color='blue')edges = nx.draw_networkx_edges(graph, pos=pos, alpha=0.1)  # 减少alpha以降低边的透明度plt.axis('off')  # 关闭坐标轴plt.savefig(save_path, format='PNG')  # 先保存可视化结果到文件plt.show()  # 然后显示图像plt.close()  # 关闭图形,防止重复显示# 修改后的可视化嵌入函数,实现三维可视化
def visualize_embeddings_3d(model, graph, save_path='embeddings_visualization_3d.png'):# 获取嵌入向量embeddings = np.array([model.wv[str(node)] for node in graph.nodes()])# 使用PCA进行到3维的降维from sklearn.decomposition import PCApca = PCA(n_components=3)embeddings_3d = pca.fit_transform(embeddings)# 创建3D绘图fig = plt.figure(figsize=(20, 16))ax = fig.add_subplot(111, projection='3d')# 获取节点的三维坐标xs = embeddings_3d[:, 0]ys = embeddings_3d[:, 1]zs = embeddings_3d[:, 2]# 绘制节点ax.scatter(xs, ys, zs, c='blue', marker='o')ax.set_xlabel('PCA1')ax.set_ylabel('PCA2')ax.set_zlabel('PCA3')plt.title('3D visualization of Node Embeddings')plt.savefig(save_path, format='PNG')  # 保存可视化结果到文件plt.show()  # 显示图像plt.close()  # 关闭图形,防止重复显示def save_embeddings_to_json(graph, model, json_file_path, output_file):# 首先,读取原始的JSON文件来获取对象间的关系with open(json_file_path, 'r') as file:json_list = json.load(file)output_data = []for item in json_list:object1, object2 = item['object1'], item['object2']# 对于每对对象,获取其嵌入向量properties1 = model.wv[object1].tolist() if object1 in model.wv else Noneproperties2 = model.wv[object2].tolist() if object2 in model.wv else None# 构建新的字典项modified_item = {"object1": object1,"properties1": properties1,"object2": object2,"properties2": properties2,"relationship": item.get('relationship')  # 假设原始JSON中包含关系字段}output_data.append(modified_item)# 将修改后的数据写入新的JSON文件with open(output_file, 'w') as f:json.dump(output_data, f, indent=4)# 主程序
if __name__ == "__main__":json_file_path = 'Node2Vec_Input.json'output_file = 'Node2Vec_Embeddings_Dim768.json'G = read_and_convert_json_to_graph(json_file_path)#节点嵌入维度为dimensionsmodel = train_node2vec(G, dimensions=768, walk_length=20, num_walks=200, p=1, q=2, workers=4)visualize_embeddings_3d(model, G, 'embeddings_visualization_Dim768_3D_20-200-1-2-4.png')# 调用新定义的函数,保存嵌入和关系到JSONsave_embeddings_to_json(G, model, json_file_path, output_file)

四、结论

  通过以上的过程,我们完整地介绍了如何使用Node2Vec进行图嵌入、如何将嵌入结果可视化以及如何保存嵌入向量。Node2Vec 提供了一种灵活且高效的方式来处理图数据,并且能够通过参数调整适应不同的图结构。通过嵌入向量,我们可以将图结构信息转化为适合机器学习模型处理的低维向量形式,从而为分类、聚类、推荐系统等任务提供支持。

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

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

相关文章

10款好用的开源 HarmonyOS 工具库

大家好,我是 V 哥,今天给大家分享10款好用的 HarmonyOS的工具库,在开发鸿蒙应用时可以用下,好用的工具可以简化代码,让你写出优雅的应用来。废话不多说,马上开整。 1. efTool efTool是一个功能丰富且易用…

Kotlin:2.0.20 的新特性

一、概述 Kotlin 2.0.20英文版官方文档 Kotlin 2.0.20发布了!这个版本包括对Kotlin 2.0.0的性能改进和bug修复,我们在其中宣布Kotlin K2编译器为Stable。以下是本次发布的一些亮点: 数据类复制函数将具有与构造函数相同的可见性来自默认目标层次结构的源集的静态访…

Python批量下载PPT模块并实现自动解压

日常工作中,我们总是找不到合适的PPT模板而烦恼。即使有免费的网站可以下载,但是一个一个地去下载,然后再批量解压进行查看也非常的麻烦,有没有更好方法呢? 今天,我们利用Python来爬取一个网站上的PPT&…

HTML+CSS基础用法介绍五

目录: 结构伪类选择器盒子模型-边框线盒子模型-内边距盒子模型-解决盒子被撑大盒子模型-外边距与版心居中小知识:清除浏览器中所有标签的默认样式内容溢出控制显示方式盒子模型-圆角 🐎正片开始 结构伪类选择器 什么是结构伪类选择器&…

全新一区PID搜索算法+TCN-LSTM+注意力机制!PSA-TCN-LSTM-Attention多变量时间序列预测(Matlab)

全新一区PID搜索算法TCN-LSTM注意力机制!PSA-TCN-LSTM-Attention多变量时间序列预测(Matlab) 目录 全新一区PID搜索算法TCN-LSTM注意力机制!PSA-TCN-LSTM-Attention多变量时间序列预测(Matlab)效果一览基本…

66 使用注意力机制的seq2seq_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录动机加入注意力总结代码定义注意力解码器训练小结练习 我们来真的看一下实际应用中,key,value,query是什么东西,但是取决于应用场景不同,这三个东西会产生变化。先将放在seq2seq这个…

Linux dlsym符号查找疑惑分析

dlsym 函数是 Linux 下动态链接库(shared library)编程中的一个重要函数。它用于在运行时获取动态链接库中符号的地址,通常用于获取函数指针或变量的地址。 以下是 dlsym 函数的基本用法和示例。 1. 函数原型 void *dlsym(void *handle, c…

如何实现事件流操作

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了通道相关的内容,本章回中将介绍StreamProvider组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 在Flutter中Stream是经常使用的组件,对该组件的监听可void main() {///让状态栏和程序的appBar融为一体…

海龟绘图画小汽车

1、效果图: 2、完整代码 import turtlet turtle.Turtle() #创建一个新的画布对象t.penup() t.goto(0,80) t.pendown()t.fillcolor("red") t.begin_fill() t.lt(180) t.fd(60) t.lt(45) t.fd(113) t.rt(45) t.fd(80) t.lt(90) t.fd(80) t.…

常见的VPS或者独立服务器的控制面板推荐

随着越来越多的企业和个人转向VPS和独立服务器以获得更高的性能和灵活性,选择合适的控制面板变得尤为重要。一个好的控制面板可以大大简化服务器管理,提高工作效率。本篇文章将介绍2024年最值得推荐的VPS控制面板,帮助您做出明智的选择。 1.…

论文阅读(十一):CBAM: Convolutional Block Attention Module

文章目录 IntroductionConvolutional Block Attention ModuleExperimentsConclusion 论文题目:CBAM: Convolutional Block Attention Module(CBAM:卷积注意力机制)   论文链接:点击跳转   代码链接:Git…

关于BSV区块链覆盖网络的常见问题解答(上篇)

​​发表时间:2024年9月20日 在BSV区块链上的覆盖网络服务为寻求可扩展、安全、高效交易处理解决方案的开发者和企业家开辟了新的视野。 作为开创性的曼达拉升级的一部分,覆盖网络服务提供了一个强大的框架,用于管理特定类型的交易和数据访问…

新编英语语法教程

新编英语语法教程 1. 新编英语语法教程 (第 6 版) 学生用书1.1. 目录1.2. 电子课件 References A New English Grammar Coursebook 新编英语语法教程 (第 6 版) 学生用书新编英语语法教程 (第 6 版) 教师用书 1. 新编英语语法教程 (第 6 版) 学生用书 https://erp.sflep.cn/…

3.点位管理改造-列表查询——帝可得管理系统

目录 前言一、与页面原型差距1.现在:2.目标:3. 存在问题: 二、修改1.重新设计SQL语句2.修改mapper层,使用Mybatis中的嵌套查询3.修改service层4. 修改controller层5.前端修改6.补充区域查看详情7.数据完整性 前言 提示&#xff1…

《OpenCV 计算机视觉》—— 视频背景建模

文章目录 一、背景建模的目的二、背景建模的方法三、背景建模的步骤四、注意事项五、代码实现 一、背景建模的目的 视频背景建模的主要目的是从视频序列中提取出静态背景,以便将动态的前景对象与静态的背景进行分离。这有助于进一步分析和处理视频内容,…

【Mybatis篇】Mybatis的关联映射详细代码带练 (多对多查询、Mybatis缓存机制)

🧸安清h:个人主页 🎥个人专栏:【计算机网络】,【Mybatis篇】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 目录 🎯一.关联映射概述 &#x1f6a…

RTSP协议讲解

1.RTSP协议 rtsp,英文全称 Real Time Streaming Protocol,RFC2326,实时流传输协议,是 TCP/IP 协议体系中的一个应用层协议。 RTSP 交互流程 1)OPTIONS C--->S 客户端向服务器端发现 OPTIONS,请求可用…

html中的文本标签(含标签的实现案例)

目录 1.标题标签 2.标题标签的align属性 3.段落标签 4.水平线标签hr 5.换行标签br 6.文本样式标签font ​编辑7.文本格式化标签 8.文本语义标签 1)时间time标签 2)文本高亮Mark标签 3)cite标签 9.特殊字符标签 10.图像标签img 附录&#xff…

基于微信小程序的旅游拼团系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…

gitee公钥设置、创建库及使用

简介 一、如何安装git 使用gitee,需要先安装git工具。 工具网站地址:https://git-scm.com/downloads 安装完成后,在terminal命令行输入git --version可以查看到git的版本。 二、登录gitee 我们先在 gitee上注册账号并登录。gitee官网&#x…