图神经网络实战——基于DeepWalk创建节点表示

图神经网络实战——基于DeepWalk创建节点表示

    • 0. 前言
    • 1. Word2Vec
      • 1.1 CBOW 与 skip-gram
      • 1.2 构建 skip-gram 模型
      • 1.3 skip-gram 模型
      • 1.4 实现 Word2Vec 模型
    • 2. DeepWalk 和随机行走
    • 3. 实现 DeepWalk
    • 小结
    • 系列链接

0. 前言

DeepWalk 是机器学习 (machine learning, ML) 技术在图数据中的成功应用之一,其引入了嵌入等重要概念,这些概念是图神经网络 (Graph Neural Network, GNN) 的核心。与传统的神经网络不同,这种架构的目标是产生表示 (representations),然后将其传递给其他模型执行下游任务(例如节点分类)时使用。
在本节中,我们将了解 DeepWalk 架构及其两个主要组件: Word2Vec 和随机游走 (random walks)。首先介绍 Word2Vec 架构的工作原理,并重点介绍 skip-gram 模型,并在自然语言处理 (natural language processing, NLP) 任务中使用 gensim 库实现 skip-gram 模型,以了解其使用方法。然后,我们将重点研究 DeepWalk 算法,学习如何使用分层 softmax (hierarchical softmax, H-Softmax) 提高性能。然后在图上实现随机游走,最后使用 “Zachary’s Karate Club” 数据集实现一个端到端的监督节点分类模型。

1. Word2Vec

理解 DeepWalk 算法的第一步是了解其主要组成部分: Word2VecWord2VecNLP 领域最具影响力的深度学习技术之一,该技术由 Tomas Mikolov 等人于 2013 年提出,是一种利用大规模文本数据集将单词转化为向量( vectors,也称为嵌入,embeddings )表示的技术,这种单词表示可用于情感分类等下游任务。
使用 Word2Vec 将单词转化为向量的示例如下:
v e c ( k i n g ) = [ − 2.0 , 4.2 , 0.5 ] v e c ( q u e e n ) = [ − 1.8 , 2.7 , 1.4 ] v e c ( m a n ) = [ 2.9 , − 1.0 , − 1.9 ] v e c ( w o m a n ) = [ 2.8 , − 2.6 , − 1.0 ] vec(king) = [−2.0, 4.2, 0.5]\\ vec(queen) = [−1.8, 2.7, 1.4]\\ vec(man) = [2.9,−1.0, −1.9]\\ vec(woman) = [2.8,−2.6,−1.0]\\ vec(king)=[2.0,4.2,0.5]vec(queen)=[1.8,2.7,1.4]vec(man)=[2.9,1.0,1.9]vec(woman)=[2.8,2.6,1.0]

在以上例子中,就欧氏距离而言,kingqueen 的词向量比 kingwoman 的词向量更接近。在实践中,我们通常会使用其他更精确的度量方法来衡量这些词的相似度,例如常用的余弦相似度 (cosine similarity)。余弦相似度关注的是向量之间的角度,而不考虑它们的大小(长度):
c o s i n e s i m i l a r i t y ( A ⃗ , B ⃗ ) = c o s ( θ ) = A ⃗ ⋅ B ⃗ ∣ ∣ A ⃗ ∣ ∣ ⋅ ∣ ∣ B ⃗ ∣ ∣ cosine\ similarity(\vec A,\vec B)=cos(\theta)=\frac {\vec A \cdot \vec B} {||\vec A||\cdot ||\vec B||} cosine similarity(A ,B )=cos(θ)=∣∣A ∣∣∣∣B ∣∣A B
Word2Vec 能够解决类比问题,最著名的例子是,它可以回答 "man is to woman, what king is to ___?"的问题。计算方法如下:
v e c ( k i n g ) − v e c ( m a n ) + v e c ( w o m a n ) ≈ v e c ( q u e e n ) vec(king)-vec(man)+vec(woman)≈vec(queen) vec(king)vec(man)+vec(woman)vec(queen)
当然这种性质并不适用于所有类比问题,但这一特性可以为嵌入算术运算带来有趣的应用。

1.1 CBOW 与 skip-gram

为了生成这些向量,模型必须在一个前置任务上进行训练。任务本身并不需要有实际意义,其唯一目标就是生成高质量的嵌入向量。在实践中,这个任务通常与根据特定上下文预测单词相关。通常,可以使用两种具有类似任务的架构:

  • 连续词袋 (continuous bag-of-words, CBOW) 模型: 模型的训练目标是利用周围上下文(目标单词前后的单词)预测一个单词。上下文单词的顺序并不重要,因为它们的嵌入向量会在模型中求和。实践表明,使用预测单词前后的四个单词进行预测时,可以获得更好的结果
  • 连续 skip-gram (continuous skip-gram, skip-gram)模型: 模型的训练目标是根据一个输入单词预测其上下文单词。增加上下文单词的范围可以获得更好的嵌入向量,但也会增加训练时间

两种模型的输入和输出如下所示:

模型对比

通常,CBOW 模型的训练速度更快,但是由于 skip-gram 模型具有学习不常见单词的能力,因此更加准确。

1.2 构建 skip-gram 模型

由于 DeepWalk 采用的是 skip-gram 架构,因此我们现在将重点学习 skip-gram 模型。skip-gram 使用具有以下结构的单词对:(target word, context word),其中 target word 是输入词,context word 是要预测的词。同一目标词的 skip-gram 数量取决于参数上下文大小 (context size),如下图所示:

Skip-gram

这一单词对结构可以从单个句子扩展至整个文本语料库。为了节省内存,我们将同一目标词的所有上下文词存储在一个列表中。接下来,我们以整个段落为例构建单词对,为存储在 text 变量中的整个段落创建 skip-gram 单词对。将 CONTEXT_SIZE 变量设置为 2,即查看目标单词前后的两个单词。

(1) 导入所需库:

import numpy as np

(2)CONTEXT_SIZE 变量设置为 2,并导入要分析的文本 text

CONTEXT_SIZE = 2
text = """Tears came to my eyes as I realized what I had been a fool to judge Al as a failure. He had not left any material possessions behind. But he had been a kind loving father, and left behind his best love.""".split()

(3) 通过一个简单的 for 循环创建 skip-gram 单词对,以考虑 text 中的每个单词。使用列表推导式生成上下文单词,并存储在 skipgrams 列表中:

# Create skipgrams
skipgrams = []
for i in range(CONTEXT_SIZE, len(text) - CONTEXT_SIZE):array = [text[j] for j in np.arange(i - CONTEXT_SIZE, i + CONTEXT_SIZE + 1) if j != i]skipgrams.append((text[i], array))

(4) 使用 print() 函数查看生成的 skipgrams

print(skipgrams[0:2])

输出结果如下,观察这两个目标单词及其相应的上下文可以了解 Word2Vec 的输入-输出形式:

[('to', ['Tears', 'came', 'my', 'eyes']), ('my', ['came', 'to', 'eyes', 'as'])]

1.3 skip-gram 模型

Word2Vec 的目标是生成高质量的单词嵌入。为了学习这些嵌入,skip-gram 模型的训练目标是根据目标词预测正确的上下文单词。
假设,我们有一个由 N N N 个单词组成的序列 w 1 , w 2 , . . . , w N w_1, w_2 ,..., w_N w1,w2,...,wN。在给定单词 w 2 w_2 w2 的情况下,得到单词 w 2 w_2 w2 的概率为 p ( w 2 ∣ w 1 ) p(w_2|w_1) p(w2w1)。我们的目标是最大化整个文本中给定目标单词时得到每个上下文单词的概率之和:
1 N ∑ n = 1 N ∑ − c ≤ j ≤ c , j ≠ 0 log ⁡ p ( w n + j ∣ w n ) \frac 1 N\sum_{n=1}^N\sum_{-c≤j≤c,j≠0}\log p(w_{n+j}|w_n) N1n=1Ncjc,j=0logp(wn+jwn)
其中 c c c 是上下文向量的大小。那么,为什么我们要在以上等式中使用对数概率?将概率转换为对数概率是机器学习中的常用技术,主要有两个原因:

  • 乘法的计算成本比加法高,而使用对数可以将乘法转换为加法(除法转换为减法),因此计算对数概率更快:
    log ⁡ ( A × B ) = log ⁡ ( A ) + log ⁡ ( B ) \log (A\times B)=\log(A)+\log(B) log(A×B)=log(A)+log(B)
  • 计算机存储非常小的数字(如 3.14e-128 )的方式并不完全准确,当事件发生的可能性极小时,这些微小的误差就会累积起来,使最终结果产生偏差。而使用对数则不同,当结果同样为 3.14e-128 时,使用对数后结果为 -127.5 (log3.14e-128=-127.5)

总的来说,将概率转换为对数概率可以在不改变初始目标的情况下提高速度和准确性。

原始 skip-gram 模型使用 softmax 函数来计算给定目标单词嵌入 h t h_t ht 的情况下,上下文单词嵌入 h c h_c hc 的概率:
p ( w c ∣ w t ) = exp ⁡ ( h c h t T ) ∑ i = 1 ∣ V ∣ exp ⁡ ( h i h t T ) p(w_c|w_t)=\frac {\exp(h_ch_t^T)}{\sum_{i=1}^{|V|}\exp(h_ih_t^T)} p(wcwt)=i=1Vexp(hihtT)exp(hchtT)
其中, V V V 是大小为 ∣ V ∣ |V| V 的词汇表,词汇表 (vocabulary) 是指在一个文本语料库中出现的所有单词的集合。可以使用集合数据结构去除重复的单词,获得词汇表:

vocab = set(text)
VOCAB_SIZE = len(vocab)
print(f"Length of vocabulary = {VOCAB_SIZE}")
# Length of vocabulary = 33

得到词汇表大小后,还需要定义参数 N N N,用于表示单词向量的维度。通常情况下,这个值设置在 1001000 之间。在本节中,由于数据集的规模有限,将其设置为 10
skip-gram 模型仅由两层组成:

  • 权重为 W e m b e d W_{embed} Wembed 的投影层 (projection layer),将独热 (one-hot) 编码词向量作为输入,并返回相应的 N N N 维词嵌入。它就像一个简单的查找表,存储预定维度的嵌入
  • 权重为 W o u t p u t W_{output} Woutput 的全连接层 (fully connected layer),将词嵌入作为输入,并输出 ∣ V ∣ |V| Vlogits。对预测结果应用 softmax 函数,将 logits 转换为概率

skip-gram 中没有激活函数:Word2Vec 是一种线性分类器,可以模拟单词之间的线性关系。
将独热编码的单词向量称为输入 x x x,相应的单词嵌入可以通过简单的投影计算得出:
h = W e m b e d T ⋅ x h=W_{embed}^T\cdot x h=WembedTx
利用 skip-gram 模型,可以将以上概率改写如下:
p ( w c ∣ w t ) = exp ⁡ ( W o u t p u t T ⋅ x ) ∑ i = 1 ∣ V ∣ exp ⁡ ( W o u t p u t ( i ) ⋅ h ) p(w_c|w_t)=\frac {\exp(W_{output}^T\cdot x)} {\sum_{i=1}^{|V|}\exp(W_{output_{(i)}}\cdot h)} p(wcwt)=i=1Vexp(Woutput(i)h)exp(WoutputTx)
skip-gram 模型会输出一个 ∣ V ∣ |V| V 维向量,它是词汇表中每个单词的条件概率:
w o r d 2 v e c ( w t ) = [ p ( w 1 ∣ w t ) p ( w 2 ∣ w t ) ⋮ p ( w ∣ v ∣ ∣ w t ) ] word2vec(w_t)=\begin{bmatrix} p(w_1|w_t)\\ p(w_2|w_t)\\ \vdots \\ p(w_{|v|}|w_t) \end{bmatrix} word2vec(wt)= p(w1wt)p(w2wt)p(wvwt)
在训练过程中,这些概率会与正确的独热编码目标单词向量进行比较。这些值之间的差异(由损失函数计算,如交叉熵损失)通过网络反向传播,以更新权重并获得更好的预测结果。

1.4 实现 Word2Vec 模型

Word2Vec 的整体架构如下所示,包括两个权重矩阵(包括 W e m b e d W_{embed} Wembed 和 $W_{output} )和最后的 softmax 层:

Word2Vec 架构

接下来,使用 gensim 库实现 Word2Vec 模型,然后根据上一小节的文本构建词汇表并训练模型。gensim 库的安装和其他第三方库一样,可以在 shell 中执行以下命令进行安装:

pip install gensim

(1) 导入 Word2Vec 类:

from gensim.models.word2vec import Word2Vec

(2) 使用对象 Word2Vec 和参数 sg=1 (skip-gram = 1) 初始化 skip-gram 模型:

model = Word2Vec([text],sg=1,   # Skip-gramvector_size=10,min_count=0,window=2,workers=1)

(3) 检查权重矩阵 W_embed 的形状,其应该与词汇量大小和词嵌入维度相对应:

print(f'Shape of W_embed: {model.wv.vectors.shape}')
# Shape of W_embed: (33, 10)

(4) 对模型训练 10epoch

model.train([text], total_examples=model.corpus_count, epochs=10)

(5) 最后,打印一个单词嵌入,观察训练结果:

print('\nWord embedding =')
print(model.wv[0])

输出结果如下:

Word embedding =
[-0.00495417 -0.00025058  0.05408746  0.08913884 -0.09218638 -0.071533940.06835324  0.09274287 -0.05681597 -0.04169786]

虽然这种方法在处理小词汇量时效果很好,但在大多数情况下,对数百万个单词(词汇量)应用完整 softmax 函数的计算成本太高,这一直是开发精确语言模型的限制因素之一,但我们可以通过其他方法来解决这个问题。
Word2VecDeepWalk 通过实现 H-Softmax 技术解决此问题。与直接计算每个单词的概率的 softmax 不同,H-Softmax 技术采用二叉树结构,其中叶子节点是单词。此外,还可以使用哈夫曼树,将不常见的单词存储在比常见单词更深的层次上。在大多数情况下,这种方法可以将单词预测速度提高至少 50 倍。在 gensim 中,可以通过指定 hs=1 使用 H-Softmax

2. DeepWalk 和随机行走

DeepWalk2014 年由 Perozzi 等人提出,并很快在图学习中大受欢迎,它在多个数据集上的表现始终优于其他方法。虽然之后研究人员又提出了其他性能更高的架构,但 DeepWalk 作为一种简单可靠的基准方法,能够快速实现并解决大量问题。
DeepWalk 的目标是以无监督的方式生成高质量的节点特征表示。这一架构在很大程度上受到了 NLPWord2Vec 的启发。但 DeepWalk 所用的数据集由节点组成,而不是单词,因此我们想要使用随机游走来生成类似句子的有意义的节点序列。句子和图之间的联系如下所示:

句子和图

随机游走是通过在每一步随机选择一个相邻节点而生成的节点序列。因此,节点可以在同一序列中出现多次。
即使节点是随机选择的,但它们经常一起出现在一个序列中,就意味着它们彼此接近。根据网络同质性 ( network homophily) 假说,相互接近的节点是相似的。这在社交网络中尤为明显,因为在社交网络中,人们会倾向于与朋友和家人建立联系。
DeepWalk 算法的核心理念在于:当节点彼此接近时,我们希望相似度得分较高;相反,当节点之间距离较远时,我们希望相似度得分较低。接下来,我们使用 networkx 库实现随机游走函数。

(1) 导入所需的库:

import networkx as nx
import matplotlib.pyplot as plt
import random

(2) 利用 erdos_renyi_graph 函数生成一个随机图,图中节点数量固定 (10),节点之间建立边的概率为 0.3

G = nx.erdos_renyi_graph(10, 0.3, seed=1, directed=False)

(3) 绘制所生成的随机图:

nx.draw_networkx(G, pos=nx.spring_layout(G))
plt.axis('off')
plt.show()

随即图

(4) 实现随机游走函数,函数使用两个参数:起始节点 (start) 和游走长度 (length)。每走一步,我们都会使用 np.random.choice 随机选择一个相邻节点,直到游走完成:

def random_walk(start, length):walk = [str(start)]  # starting nodefor i in range(length):neighbors = [node for node in G.neighbors(start)]next_node = np.random.choice(neighbors, 1)[0]walk.append(str(next_node))start = next_nodereturn walk

(5) 打印函数执行结果,起始节点为 0,长度为 10

print(random_walk(0, 10))

输出结果如下:

['0', '9', '0', '4', '3', '4', '3', '6', '2', '5', '6']

可以看到某些节点,如 09 经常出现在一起。考虑到这是一个同构图,这意味着它们是相似的,这正是 DeepWalk 试图捕捉的关系类型。

3. 实现 DeepWalk

了解了 DeepWalk 架构中的每个组件后,实现 DeepWalk 解决一个实际的机器学习问题。我们使用 Zachary’s Karate Club 数据集,它简单地表示了 Wayne W. Zachary 研究的一个空手道俱乐部内部的关系。这是一种社交网络,其中的每个节点都是一个成员,而在俱乐部之外进行互动的成员则被连接在一起。
在本例中,俱乐部被分为两组:我们希望通过查看每个成员的连接,将每个成员分配到正确的组中(即节点分类,node classification)。

(1) 使用 nx.karate_club_graph() 导入数据集:

G = nx.karate_club_graph()

(2) 将字符串类标签转换成数值 (Mr. Hi = 0Officer = 1):

labels = []
for node in G.nodes:label = G.nodes[node]['club']labels.append(1 if label == 'Officer' else 0)

(3) 使用新标签绘制图:

plt.axis('off')
nx.draw_networkx(G, pos=nx.spring_layout(G, seed=0), node_color=labels)
plt.show()

标签图

(4) 接下来,生成随机游走数据集,为图中的每个节点创建 80 个长度为 10 的随机游走序列:

walks = []
for node in G.nodes:for _ in range(80):walks.append(random_walk(node, 10))

(5) 打印一个随机游走序列,验证其正确性:

print(walks[0])
# ['0', '1', '30', '1', '3', '12', '3', '7', '3', '0', '17']

(6) 实现 Word2Vec,使用 skip-gram 模型,可以调整其他参数,以提高嵌入质量:

model = Word2Vec(walks,hs=1,   # Hierarchical softmaxsg=1,   # Skip-gramvector_size=100,window=10,workers=1)

(7) 在生成的随机游走序列上对模型进行简单的训练:

# Build vocabulary
model.build_vocab(walks)
# Train model
model.train(walks, total_examples=model.corpus_count, epochs=30, report_delay=1)

(8) 模型训练完成后,可以将其应用在不同任务中。例如,查找与给定节点最相似的节点(根据余弦相似度):

print('Nodes that are the most similar to node 0:')
for similarity in model.wv.most_similar(positive=['0']):print(f'   {similarity}')

输出结果如下所示:

输出

另一个重要应用是计算两个节点之间的相似度得分:

print(f"\nSimilarity between node 0 and 4: {model.wv.similarity('0', '4')}")
# Similarity between node 0 and 4: 0.6553224921226501

可以使用 t-distributed stochastic neighbor embedding (t-SNE) 绘制嵌入结果,从而将这些高维向量可视化为二维图像:

(1)sklearn 中导入 TSNE 类:

from sklearn.manifold import TSNE

(2) 创建两个数组:一个用于存储单词嵌入,另一个用于存储标签:

nodes_wv = np.array([model.wv.get_vector(str(i)) for i in range(len(model.wv))])
labels = np.array(labels)

(3) 在嵌入上训练 2 维 (n_components=2) t-SNE 模型:

tsne = TSNE(n_components=2,learning_rate='auto',init='pca',random_state=0).fit_transform(nodes_wv)

(4) 将训练好的 t-SNE 模型生成的二维向量与相应的标签绘制成二维图像:

plt.figure(figsize=(6, 6))
plt.scatter(tsne[:, 0], tsne[:, 1], s=100, c=labels)
plt.show()

分类结果

可以看到使用一条简单的线就能够将两个类别区分开来。只要有足够的训练数据,简单的机器学习算法就能对这些节点进行分类。接下来,我们实现一个分类器,并对节点嵌入进行训练。

(1)sklearn 中导入随机森林 (Random Forest) 模型,使用准确率作为模型评估的指标:

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

(2) 将嵌入数据分成两组:训练数据和测试数据。一个简单的方法是创建如下掩码:

train_mask = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
test_mask = [0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33]

(3) 在训练数据上训练随机森林分类器:

clf = RandomForestClassifier(random_state=0)
clf.fit(nodes_wv[train_mask], labels[train_mask])

(4) 在测试数据上根据准确率评估训练好的模型:

y_pred = clf.predict(nodes_wv[test_mask])
acc = accuracy_score(y_pred, labels[test_mask])
print(f'Accuracy = {acc*100:.2f}%')
# Accuracy = 95.45%

可以看到,模型准确率为 95.45%,虽然仍有改进的余地,但本例展示了 DeepWalk 的两个应用:

  • 使用嵌入和余弦相似性发现节点之间的相似性(无监督学习)
  • 将嵌入数据集用作节点分类等监督任务

学习节点表示可以为设计更深入、更复杂的架构提供了更大的灵活性。

小结

在本节中,我们了解了 DeepWalk 架构及其主要组件。然后,使用随机游走将图数据转化为序列,并应用了 Word2Vec 算法,使用图的拓扑信息创建节点嵌入,得到的嵌入结果可用于发现节点间的相似性,或作为其他算法的输入。最后,我们使用监督方法解决了节点分类问题。

系列链接

图神经网络实战——图神经网络(Graph Neural Networks, GNN)基础
图神经网络实战——图论基础

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

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

相关文章

[Angular 基础] - routing 路由(上)

[Angular 基础] - routing 路由(上) 之前部分 Angular 笔记: [Angular 基础] - 生命周期函数 [Angular 基础] - 自定义指令,深入学习 directive [Angular 基础] - service 服务 终于到 routing 了……这部分的内容比我想象的要复杂很多,果…

力扣每日一题 用队列实现栈 模拟

Problem: 225. 用队列实现栈 文章目录 思路复杂度Code 思路 👨‍🏫 力扣官解 辅助队列存栈顶元素主队列存逆序序列 复杂度 时间复杂度: 添加时间复杂度, 示例: O ( n ) O(n) O(n) 空间复杂度: 添加空间复杂度, 示例: O ( …

js监听网页iframe里面元素变化其实就是监听iframe变化

想要监听网页里面iframe标签内容变化,需要通过监听网页dom元素变化,然后通过查询得到iframe标签,再通过iframe.contentWindow.document得到ifram内的document,然后再使用选择器得到body元素,有了body元素,就…

Java和JavaScript之间的主要区别与联系

目录 概况 主要区别 联系 总结 概况 Java和JavaScript,尽管名字相似,但它们在编程世界中却扮演着截然不同的角色。Java,一种强类型、面向对象的编程语言,广泛应用于企业级应用和安卓应用开发。它的设计理念是一次编写&#x…

详解 JavaScript 中的数组

详解 JavaScript 中的数组 创建数组 注:在JS中的数组不要求元素的类型,元素类型可以一样,也可以不一样 1.使用 new 关键字创建 let array new Array()2.使用字面量方式创建(常用) let array1 [1,2,3,"4"]获取数组元素 使用下…

python进阶:可迭代对象和迭代器

一、Iterable(可迭代对象) 1、可迭代对象:能够进行迭代操作的对象。 可以理解为:能够使用for循环遍历的都是可迭代对象;**所有的可迭代对象,偶可以用内置函数iter转换为迭代器** 2、可迭代对象包括&…

蓝桥杯题练习:平地起高楼

题目要求 function convertToTree(regions, rootId "0") {// TODO: 在这里写入具体的实现逻辑// 将平铺的结构转化为树状结构,并将 rootId 下的所有子节点数组返回// 如果不存在 rootId 下的子节点,则返回一个空数组}module.exports convert…

网络防御保护——课堂笔记

一.内容安全 攻击可能只是一个点,防御需要全方面进行 IAE引擎 DFI和DPI技术 --- 深度检测技术 DPI ---深度包检测技术 ---主要针对完整的数据包(数据包分片,分段需要重组),之后对数据包的内容进行识别。(应…

ifcplusplus 示例 函数中英文 对照分析以及流程图

有需求,需要分析 ifc c渲染,分析完,有 230个函数,才能完成一个加载,3d加载真的是大工程! 示例代码流程图 函数中英文对照表,方便 日后开发,整理思路顺畅!!&am…

C语言——指针的进阶——第1篇——(第26篇)

坚持就是胜利 文章目录 一、字符指针1、面试题 二、指针数组三、数组指针1、数组指针的定义2、&数组名 VS 数组名3、数组指针的使用(1)二维数组传参,形参是 二维数组 的形式(2)二维数组传参,形参是 指针…

【RT-Thread应用笔记】英飞凌PSoC 62 + CYW43012 WiFi延迟和带宽测试

文章目录 一、安装SDK二、创建项目三、编译下载3.1 编译代码3.2 下载程序 四、WiFi测试4.1 扫描测试4.2 连接测试 五、延迟测试5.1 ping百度5.2 ping路由器 六、带宽测试6.1 添加netutils软件包6.2 iperf命令参数6.3 PC端的iperf6.4 iperf测试准备工作6.5 进行iperf带宽测试6.6…

Muduo库编译学习(1)

1.muduo库简介 muduo是由Google大佬陈硕开发,是一个基于非阻塞IO和事件驱动的现代C网络库,原生支持one loop per thread这种IO模型,该库只支持Linux系统,网上大佬对其褒贬不一,作为小白用来学习就无可厚非了。 git仓库…

b站小土堆pytorch学习记录——P14 torchvision中的数据集使用

文章目录 一、前置知识如何查看torchvision的数据集 二、代码(附注释)及运行结果 一、前置知识 如何查看torchvision的数据集 (1)打开官网 https://pytorch.org/ pytorch官网 (2)打开torchvision 在Do…

Unity游戏输入系统(新版+旧版)

使用新版还是旧版 旧版 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c5 : MonoBehaviour {void Start(){}void Update(){// 注意要在游戏中 点鼠标键盘进行测试// 鼠标// 0左键 1右键 2滚轮if (Input.GetMouseButtonDown(0)…

【javaSE-语法】lambda表达式

【javaSE-语法】lambda表达式 1. 先回忆一下:1.1 接口不能直接通过关键字new进行实例化1.2 函数式接口1.3 匿名内部类1.31 匿名内部类在代码中长啥样?1.32 构造一个新的对象与构造一个扩展了某类的匿名内部类的对象,两者有什么区别&#xff1…

midjourney提示词语法

更高级的提示可以包括一个或多个图像URL、多个文本短语和一个或更多个参数 Image Prompts 可以将图像URL添加到提示中,以影响最终结果的样式和内容。图像URL总是位于提示的前面。 https://docs.midjourney.com/image-prompts Text Prompt 要生成的图像的文本描述。…

YOLOv6、YOLOv7、YOLOv8网络结构图(清晰版)

承接上一篇博客:YOLOv3、YOLOv4、YOLOv5、YOLOx的网络结构图(清晰版)_yolox网络结构图-CSDN博客 1. YOLOv6网络结构图 2. YOLOv7网络结构图 3. YOLOv8网络结构图

搭建 LNMP 架构

一 理论知识 (一)架构图 (二)CGI 由来 最早的Web服务器只能简单她响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html文件,但是后期随着网站功能增多网站开…

c++阶梯之模板初阶

1. 泛型编程 void Swap(int& x, int& y) {int tmp x;x y;y tmp; }void Swap(double& x, double& y) {double tmp x;x y;y tmp; }void Swap(char& x, char& y) {char tmp x;x y;y tmp; } int main() {int a 10, b 20;double c 1.1, d 2.2…

《Spring Security 简易速速上手小册》第7章 REST API 与微服务安全(2024 最新版)

文章目录 7.1 保护 REST API7.1.1 基础知识详解7.1.2 重点案例:使用 JWT 进行身份验证和授权案例 Demo 7.1.3 拓展案例 1:API 密钥认证案例 Demo测试API密钥认证 7.1.4 拓展案例 2:使用 OAuth2 保护 API案例 Demo测试 OAuth2 保护的 API 7.2 …