图神经网络实战(9)——GraphSAGE详解与实现

图神经网络实战(9)——GraphSAGE详解与实现

    • 0. 前言
    • 1. GraphSAGE 原理
      • 1.1 邻居采样
      • 1.2 聚合
    • 2. 构建 GraphSAGE 模型执行节点分类
      • 2.1 数据集分析
      • 2.2 构建 GraphSAGE 模型
    • 3. PinSAGE
    • 小结
    • 系列链接

0. 前言

GraphSAGE 是专为处理大规模图而设计的图神经网络 (Graph Neural Networks, GNN) 架构。在科技行业,可扩展性是推动系统增长的关键驱动力。因此,系统的设计本质上就是为了容纳数百万用户。与图卷积网络 (Graph Convolutional Network, GCN) 和图注意力网络 (Graph Attention Networks,GAT) 相比,这种能力要求从根本上改变 GNN 模型的工作方式。因此,GraphSAGE 自然成为 Uber EatsPinterest 等科技公司的首选架构。
在本节中,我们将了解 GraphSAGE 的两个主要思想。首先,我们将介绍邻居采样技术,这是 GraphSAGE 可扩展性的核心。然后,我们将探讨用于生成节点嵌入的三种聚合算子。除了原始 GraphSAGE,还将详细介绍 Uber EatsPinterest 提出的变体架构。最后,使用 PyTorch Geometric 实现 GraphSAGE 架构在 PubMed 数据集上执行节点分类

1. GraphSAGE 原理

Hamilton 等人于 2017 年提出了 GraphSAGE,作为大规模图(节点数超过 10 万)归纳表征学习的框架,其目标是为节点分类等下游任务生成节点嵌入。此外,它还解决了图卷积网络 (Graph Convolutional Network, GCN) 和图注意力网络 (Graph Attention Networks,GAT) 的两个问题——扩展到大规模图和高效泛化到未见过数据。在本节中,我们将通过介绍 GraphSAGE 的两个主要组件来说明如何实现 GraphSAGE

  • 邻居采样 (Neighbor sampling)
  • 聚合 (Aggregation)

1.1 邻居采样

在传统神经网络中有一个重要概念——小批量 (mini-batch) 训练,通过将数据集划分为更小的片段(称为批,batch )进行训练。梯度下降是一种在训练过程中找到最佳权重和偏置的优化算法。梯度下降有三种类型:

  • 批梯度下降 (Batch gradient descent):权重和偏置在整个数据集处理完毕后更新(每个 epoch 更新一次)。这是在之前的图卷积网络 (Graph Convolutional Network, GCN) 模型中采用的技术,这种技术训练过程较慢,需要将数据集放在内存中
  • 随机梯度下降 (Stochastic gradient descent):针对数据集中的每个训练实例更新权重和偏置。由于误差没有进行平均,因此容易受到随机噪声的影响,但可以用于进行在线训练
  • 小批量梯度下降 (Mini-batch gradient descent): 在每个小批量数据训练结束时更新权重和偏置。这种技术训练速度更快(可使用 GPU 并行处理批数据),收敛也更稳定。此外,对于许多实际的业务场景数据而言,图的规模往往是十分巨大的,数据集往往会超出可用内存,为此采用小批量的训练方式对于大规模图数据的训练是十分必要的

在实践中, RMSpropAdam 等优化器也实现了小批量处理。表格数据集的拆分非常简单,只需选择样本即可。然而,对于图数据集来说,问题在于如何在不破坏基本连接的情况下选择节点。若操作不当,可能会得到一个孤立节点的集合,无法进行任何聚合。
回顾图神经网络 (Graph Neural Networks, GNN) 模型,每个 GNN 层都会根据节点的邻居计算节点嵌入,这意味着计算一个嵌入只需要这个节点的直接邻居( 1 跳);如果 GNN 有两个 GNN 层,就同时需要它们邻居的邻居( 2 跳),以此类推,如下所示,图中的其他部分对计算中心节点的嵌入并无关系:

邻居采样

利用这种技术,我们可以用计算图( computation graphs,或称子图)来填充批数据,计算图描述了计算节点嵌入的整个操作序列,下图以更直观的方式展示了节点 0 的计算图:

计算图

根据以上阐述,我们需要聚合 2 跳邻居,以计算 1 跳邻居的嵌入,但是对于一个大规模的图数据而言,直接使用这种设计存在以下两个问题:

  • 计算图随着跳数的增加而呈指数级增长,这会导致很高的计算复杂度
  • 度非常高的节点(如在线社交网络中的名人),也称为中心节点 (hub node) 或超级节点,会产生巨大的计算图

为了解决这些问题,必须限制计算图的大小。在 GraphSAGE 中,使用邻居采样技术来控制计算图的增长率。具体做法如下:不添加计算图中的每个邻居,而是对预定数量的邻居进行抽样。例如,在第一跳时只保留(最多)三个邻居,在第二跳时只保留五个邻居,因此,在这种情况下,计算图总节点数不会超过 3 × 5 = 15 个节点。

邻居采样

采样数越少,效率越高,但训练的随机性越大(方差越大)。此外,GNN 层数(跳数)必须保持较低水平,以避免计算图呈指数级增长。邻居采样可以处理大规模计算图,但它需要对重要信息进行剪枝处理,从而对准确率等产生负面影响。需要注意的是,计算图涉及大量冗余计算,这会降低整个过程的计算效率。
随机抽样并不是唯一可用的技术。Pinterest 改进了 GraphSAGE 架构,用于构建推荐系统,称为 PinSAGEPinSAGE 使用随机游走实现了另一种抽样方法,PinSAGE 保留了邻居数量固定的理念,但通过随机行走来检查哪些节点是最常遇到的,这种频率决定了节点的相对重要性。PinSAGE 的采样策略可以选择图中最重要的节点,实践证明,这种方法更加高效。

1.2 聚合

了解了如何选择相邻节点后,还需要计算嵌入。GraphSAGE 研究了聚合邻居操作所需的性质,并提出了几种新的聚合操作 (aggregator,也称聚合算子):

  • 平均聚合算子
  • 长短期记忆 (long short-term memory, LSTM) 聚合算子
  • 池化聚合算子

平均聚合算子取目标节点及其采样邻居的嵌入平均值,然后,用权重矩阵 W W W 对这一结果进行线性变换。平均聚合算子可以用以下公式概括,其中 σ \sigma σ 是一个非线性函数,如 ReLUtanh
h i ′ = σ ( W ⋅ m e a n j ∈ N i ( h j ) ) h'_i=\sigma(W\cdot mean_{j\in\mathcal N_i}(h_j)) hi=σ(WmeanjNi(hj))
PyTorch GeometricGraphSAGE 实现中,使用了两个权重矩阵,第一个专门用于目标节点,第二个用于邻居节点,因此聚合算子可以改写为:
h i ′ = σ ( W 1 h i + W 2 ⋅ m e a n j ∈ N i ( h j ) ) h'_i=\sigma(W_1h_i+W_2\cdot mean_{j\in\mathcal N_i}(h_j)) hi=σ(W1hi+W2meanjNi(hj))
长短期记忆 (long short-term memory, LSTM) 聚合算子基于长短期记忆 (long short-term memory, LSTM) 架构,LSTM 是一种流行的递归神经网络类型。与平均聚合算子相比,LSTM 聚合算子理论上可以区分更多的图结构,从而产生更好的嵌入。问题在于,递归神经网络只考虑输入的序列,例如一个有开头和结尾的句子。然而,节点没有任何序列,可以通过对节点的邻居进行随机排序解决这一问题。这种解决方法使我们能够使用 LSTM 架构,而无需依赖任何输入序列。
池化聚合算子分为两步工作。首先,将每个邻居的嵌入信息输入多层感知机 (Multilayer Perceptron, MLP),生成一个新的向量。然后,借鉴了卷积神经网络 (Convolutional Neural Network, CNN) 中的池化操作进行聚合,常见的如最大池化操作,即只保留每个特征的最高值。
除了这三种聚合算子外,还可以在 GraphSAGE 框架中使用其他聚合器。事实上,GraphSAGE 的核心思想主要在于其高效的邻居采样。

2. 构建 GraphSAGE 模型执行节点分类

在本节中,我们将实现 GraphSAGE 架构在 PubMed 数据集上执行节点分类。
我们已经了解了同属 Planetoid 系列的另外两个引文网络数据集——Cora 和 CiteSeer。而 PubMed 数据集则是一个类似网络,但其具有更大的规模,包含 19,717 个节点和 88,648 条边。下图展示了 Gephi 创建的 PubMed 数据集的可视化结果。

PubMed 数据集

PubMed 中节点的特征是 500 维的 TF-IDF (term frequency–inverse document frequency)加权单词向量,我们的目标是将节点正确分为三类——实验性糖尿病 (diabetes mellitus experimental)、1 型糖尿病 (diabetes mellitus type 1) 和 2 型糖尿病 (diabetes mellitus type 2)。接下来,使用 PyTorch Geometric (PyG) 逐步构建 GraphSAGE 实现节点分类。

2.1 数据集分析

(1)Planetoid 类中加载 PubMed 数据集,并打印图数据的相关信息:

from torch_geometric.datasets import Planetoid

dataset = Planetoid(root='.', name="Pubmed")
data = dataset[0]

# Print information about the dataset
print(f'Dataset: {dataset}')
print('-------------------')
print(f'Number of graphs: {len(dataset)}')
print(f'Number of nodes: {data.x.shape[0]}')
print(f'Number of features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')

# Print information about the graph
print(f'\nGraph:')
print('------')
print(f'Training nodes: {sum(data.train_mask).item()}')
print(f'Evaluation nodes: {sum(data.val_mask).item()}')
print(f'Test nodes: {sum(data.test_mask).item()}')
print(f'Edges are directed: {data.is_directed()}')
print(f'Graph has isolated nodes: {data.has_isolated_nodes()}')
print(f'Graph has loops: {data.has_self_loops()}')

输出结果如下所示:

数据集信息

可以看到,图中有 1000 个测试节点,而只有 60 个训练节点。由于只有 19,717 个节点,用 GraphSAGE 处理 PubMed 的速度非常快。

(2) GraphSAGE 框架的第一步是邻居采样。PyG 使用 NeighborLoader 类来完成这一任务,我们保留目标节点的 10 个邻居和其邻居的 10 个邻居。对 60 个目标节点进行分组,每 16 个节点为一批,最后得到四批数据:

from torch_geometric.loader import NeighborLoader
from torch_geometric.utils import to_networkx

# Create batches with neighbor sampling
train_loader = NeighborLoader(
    data,
    num_neighbors=[5, 10],
    batch_size=16,
    input_nodes=data.train_mask,
)

(3) 通过打印批数据信息,可以验证是否得到四批数据:

# Print each subgraph
for i, subgraph in enumerate(train_loader):
print(f'Subgraph {i}: {subgraph}')

批数据

(4) 每个子图包含 60 多个节点,使用 matplotlibsubplot 将它们绘制成图像:

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

# Plot each subgraph
fig = plt.figure(figsize=(16,16))
for idx, (subdata, pos) in enumerate(zip(train_loader, [221, 222, 223, 224])):
    G = to_networkx(subdata, to_undirected=True)
    ax = fig.add_subplot(pos)
    ax.set_title(f'Subgraph {idx}', fontsize=24)
    plt.axis('off')
    nx.draw_networkx(G, pos=nx.spring_layout(G), with_labels=False, node_color=subdata.y)
plt.show()

计算图

从上图可以看到,由于采用邻居抽样,子图中大多数节点的度数都是 1。因为它们的嵌入只在计算图中使用一次,以计算第二层的嵌入。

2.2 构建 GraphSAGE 模型

(1) 实现 accuracy() 函数评估模型的准确性:

import torch
import torch.nn.functional as F
from torch_geometric.nn import SAGEConv

def accuracy(pred_y, y):
    """Calculate accuracy."""
	return ((pred_y == y).sum() / len(y)).item()

(2) 使用两个 SAGEConv 层初始化 GraphSAGE 类(默认选择平均聚合算子):

class GraphSAGE(torch.nn.Module):
    """GraphSAGE"""
    def __init__(self, dim_in, dim_h, dim_out):
        super().__init__()
        self.sage1 = SAGEConv(dim_in, dim_h)
        self.sage2 = SAGEConv(dim_h, dim_out)

(3) 使用两个平均聚合算子计算嵌入,并使用一个非线性函数 (ReLU) 和一个 Dropout 层:

    def forward(self, x, edge_index):
        h = self.sage1(x, edge_index)
        h = torch.relu(h)
        h = F.dropout(h, p=0.5, training=self.training)
        h = self.sage2(h, edge_index)
        return h

(4) 由于采用小批量训练,fit() 函数需要修改为先循环 epoch 次,然后再循环批数据,以在每个批数据上训练 epoch 次,模型的度量指标必须在每个 epoch 开始时重新初始化:

    def fit(self, loader, epochs):
        criterion = torch.nn.CrossEntropyLoss()
        optimizer = torch.optim.Adam(self.parameters(), lr=0.01)

        self.train()
        for epoch in range(epochs+1):
            total_loss = 0
            acc = 0
            val_loss = 0
            val_acc = 0

第二个循环在每个批数据上训练模型:

            for batch in loader:
                optimizer.zero_grad()
                out = self(batch.x, batch.edge_index)
                loss = criterion(out[batch.train_mask], batch.y[batch.train_mask])
                total_loss += loss.item()
                acc += accuracy(out[batch.train_mask].argmax(dim=1), batch.y[batch.train_mask])
                loss.backward()
                optimizer.step()

                # Validation
                val_loss += criterion(out[batch.val_mask], batch.y[batch.val_mask])
                val_acc += accuracy(out[batch.val_mask].argmax(dim=1), batch.y[batch.val_mask])

打印模型训练过程模型性能的变化情况:

            if epoch % 20 == 0:
                print(f'Epoch {epoch:>3} | Train Loss: {loss/len(loader):.3f} | Train Acc: {acc/len(loader)*100:>6.2f}% | Val Loss: {val_loss/len(train_loader):.2f} | Val Acc: {val_acc/len(train_loader)*100:.2f}%')

(5) 实现 test() 方法,测试集不使用小批量进行测试:

    @torch.no_grad()
    def test(self, data):
        self.eval()
        out = self(data.x, data.edge_index)
        acc = accuracy(out.argmax(dim=1)[data.test_mask], data.y[data.test_mask])
        return acc

(6) 实例化一个隐藏维度为 64 的模型,并对其进行 200epoch 的训练:

# Create GraphSAGE
graphsage = GraphSAGE(dataset.num_features, 64, dataset.num_classes)
print(graphsage)

# Train
graphsage.fit(train_loader, 200)

训练过程

需要注意的是,两个 SAGEConv 层都使用默认的平均聚合算子。

(7) 最后,在测试集上对训练后的模型进行测试:

acc = graphsage.test(data)
print(f'GraphSAGE test accuracy: {acc*100:.2f}%')

# GraphSAGE test accuracy: 75.90%

可以看到,模型在测试集上的准确率为 75.90%,考虑到此数据集的训练/测试的分割方式并非最佳,这已经算是一个不错的成绩。虽然,在 PubMed 数据集上,GraphSAGE 的平均准确率低于图卷积网络 (Graph Convolutional Network, GCN) (76.2%) 和图注意力网络 (Graph Attention Networks,GAT) (77.12%),但在训练这三个模型时,我们可以感受到了 GraphSAGE 的训练速度极快。使用 GPU 进行训练,GraphSAGE 的训练速度比 GCN4 倍,比 GAT88 倍。即使 GPU 的内存不是问题,GraphSAGE 在处理大规模的图数据时也能得到更好的结果。
我们还可以使用无监督学习,在没有标签的情况下训练 GraphSAGE。这在标签缺失或标签由下游应用程序提供的情况下十分有用,但这种情况下需要使用新的损失函数,以鼓励相近的节点有相似的表示,同时确保相距较远的节点的嵌入也具有较大距离:
J G ( h i ) = − l o g ( σ ( h i T h j ) ) − Q ⋅ E j n ∼ P n ( j ) l o g ( σ ( − h i T h j n ) ) J_\mathcal G(h_i)=-log(\sigma(h_i^Th_j))-Q\cdot E_{j_n\sim P_n(j)}log(\sigma(-h_i^Th_{j_n})) JG(hi)=log(σ(hiThj))QEjnPn(j)log(σ(hiThjn))
其中, j j j 是随机行走中 u u u 的邻居, σ σ σsigmoid 函数, P n ( j ) P_n(j) Pn(j) j j j 的负采样分布, Q Q Q 是负采样的数量。
除了 GraphSAGE 外,还可以考虑采用其它方式扩展 GNN,以如下两种标准技术为例:

  • Cluster-GCN 对于如何创建小批量数据提供了不同的解决方案。它并未采用邻居采样,而是将图划分为孤立的集群,然后将这些集群作为独立的图进行处理,这可能会对生成的嵌入的质量产生负面影响
  • 简化 GNN 可以缩短训练和推理时间。在实践中,简化方法包括舍弃非线性激活函数,线性层可以通过线性代数压缩成一个矩阵乘法。当然,这些简化 GNN 在小数据集上不如完整的 GNN 准确,但在大规模图(如 Twitter )上十分高效。

3. PinSAGE

PinSAGE 是基于 GraphSAGE 的推荐系统,将无监督学习与最大间隔排名损失 (max-margin ranking loss) 函数结合起来。目标是为每个用户排列出最相关的实体(食物、餐馆、标记等),为了实现这一目标,采用最大间隔排名损失函数(通过比较正样本和负样本之间的嵌入向量来度量它们之间的相似性),并考虑了嵌入对。
具体而言,对于每个用户,PinSAGE 选择一个正样本(例如用户喜欢的实体)和多个负样本(例如用户不感兴趣的实体)。然后,它计算正样本嵌入向量与每个负样本嵌入向量之间的差异,并通过最大化它们之间的间隔来优化模型。这种损失函数的目的是鼓励模型将正样本与负样本区分开来,以便在推荐过程中能够更好地排名最相关的实体。

小结

本节介绍了 GraphSAGE 框架及其两个组成部分——邻居采样算法和三个不同的聚合算子,其中邻居采样是 GraphSAGE 能够高效处理大规模图的核心。并使用 PyTorch Geometric 构建 GraphSAGE 模型在 PubMed 数据集上执行节点分类,GraphSAGE 虽然准确率略低于 GCNGAT 模型,但它是常用于处理大规模图数据的高效框架。

系列链接

图神经网络实战(1)——图神经网络(Graph Neural Networks, GNN)基础
图神经网络实战(2)——图论基础
图神经网络实战(3)——基于DeepWalk创建节点表示
图神经网络实战(4)——基于Node2Vec改进嵌入质量
图神经网络实战(5)——常用图数据集
图神经网络实战(6)——使用PyTorch构建图神经网络
图神经网络实战(7)——图卷积网络(Graph Convolutional Network, GCN)详解与实现
图神经网络实战(8)——图注意力网络(Graph Attention Networks, GAT)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一般实现分布式锁都有哪些方式?使用 Redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?

目录 1.Redis 分布式锁 (1)Redis 最普通的分布式锁 (2)RedLock 算法 2.zk 分布式锁 3.redis 分布式锁和zk分布式锁的对比 1.Redis 分布式锁 官方叫做 RedLock 算法,是 Redis 官方支持的分布式锁算法。 这个分布式…

C++ | Leetcode C++题解之第70题爬楼梯

题目: 题解: class Solution { public:int climbStairs(int n) {double sqrt5 sqrt(5);double fibn pow((1 sqrt5) / 2, n 1) - pow((1 - sqrt5) / 2, n 1);return (int)round(fibn / sqrt5);} };

从0到1:商场导览小程序开发笔记一

背景 购物中心与商场小程序:旨在提供便捷的购物、导航、活动报名、服务查询等功能,让用户更好地体验购物和享受服务。通过提供便捷的购物、信息查询和互动预约等功能,提升了商场的服务水平和用户体验,帮助商场与消费者建立更紧密…

mq发送消息之后,业务代码回滚,导致发了一条中奖消息给用户!!

背景是这样的:在一个名为"幸运大转盘"的线上活动中,用户可以通过消耗一定的积分来参与抽奖,有机会赢取各种奖品。这个活动的后台系统使用了消息队列(MQ)来处理用户的抽奖请求和发送中奖消息。 一天&#xf…

linux(ubuntu18.04.2) Qt编译 MySQL(8.0以上版本)链接库 Qt版本 5.12.12及以上 包含Mysql动态库缺失问题

整理这篇文档的意义在于:自己走了很多弯路,淋过雨所以想为别人撑伞,也方便回顾,仅供参考 一、搭建开发环境: 虚拟机(ubuntu-20.04.6-desktop-amd64):Mysql数据库 8.0.36Workbench …

QtWindows任务栏

目录 引言任务栏进度右键菜单缩略图工具栏完整代码 引言 针对Windows系统的任务栏,Qt基于系统的原生接口封装有一些非常见类,如QWinTaskbarButton、QWinTaskbarButton、QWinThumbnailToolBar等,用于利用工具栏提供更多的信息,诸如…

开源电子邮件营销平台 listmonk 使用教程

做产品肯定要做电子邮件营销,特别是面向海外的产品,电子邮件营销已成为企业与客户沟通、建立品牌忠诚度和推动销售的重要工具,可以直接接触到目标受众,提供个性化内容,并以相对较低的成本获得可观的投资回报。你看&…

用HAL库改写江科大的stm32入门例子_1、按键控制led灯

1 如下图设置PB11 管脚 2 设置PB11为下降沿中断: 3 PA1 设置为推挽输出 4、NVIC 开启中断使能: 5、写中断事件: 完整代码如下: void EXTI15_10_IRQHandler(void) {/* USER CODE BEGIN EXTI15_10_IRQn 0 *///torning on the led…

母婴店运用商城小程序店铺的效果是什么

母婴市场规模高,还可与不少行业无缝衔接,尤其是以90后、00后为主的年轻人,在备孕生育和婴儿护理前后等整体流程往往不惜重金且时间长,母婴用品无疑是必需品,商家需要多方面拓展全面的客户及打通场景随时消费路径。 运…

24.5.5(离散化+树状数组,线段树)

星期一: dp题单 背包 第四题 混可乐 cf传送门 思路:条件可演化为每种可乐值为 ai-n,选最少的可乐使总和为0(具体可看官方题解 到这会发现背包并不适合了,其实这是道bfs伪装的背包…

【Linux网络】网络文件共享

目录 一、存储类型 二、FTP文件传输协议 2.1 FTP工作原理 2.2 FTP用户类型 2.3 FTP软件使用 2.3.1 服务端软件vsftpd 2.3.2 客户端软件ftp 2.4 FTP的应用 2.4.1 修改端口号 2.4.2 匿名用户的权限 2.4.3 传输速率 三、NFS 3.1 工作原理 3.2 NFS软件介绍 3.3 NFS配…

数据结构===二叉树

文章目录 概要二叉树的概念分类存储遍历前序中序后序 小结 概要 简单写下二叉树都有哪些内容,这篇文章要写什么 二叉树的概念分类,都有哪些二叉树遍历 对一个数据结构,最先入手的都是定义,然后才会有哪些分类,对二叉…

C语言 | Leetcode C语言题解之第70题爬楼梯

题目: 题解: int climbStairs(int n) {double sqrt5 sqrt(5);double fibn pow((1 sqrt5) / 2, n 1) - pow((1 - sqrt5) / 2, n 1);return (int) round(fibn / sqrt5); }

机器人系统ros2-开发实践05-将静态坐标系广播到 tf2(Python)-定义机器人底座与其传感器或非移动部件之间的关系

发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系非常有用。例如,最容易推断激光扫描仪中心框架中的激光扫描测量结果。 1. 创建包 首先,我们将创建一个用于本教程和后续教程的包。调用的包learning_tf2_py将依赖于geometry_msgs、pyth…

【负载均衡式在线OJ项目day1】项目结构

一.功能 查看题目列表,在线编程,判题功能,即leetcode的部分功能 二.宏观结构 整个项目是BS模式,客户端是浏览器,和用户交互并向服务器发起请求。 服务端从功能上来说分为两个模块,第一个是OJServer&…

FFmpeg———encode_video(学习)

目录 前言源码函数最终效果 前言 encode_video:实现了对图片使用指定编码进行编码,生成可播放的视频流,编译时出现了一些错误,做了一些调整。 基本流程: 1、获取指定的编码器 2、编码器内存申请 3、编码器上下文内容参数设置 4、…

平平科技工作室-Python-超级玛丽

一.准备图片 放在文件夹取名为images 二.准备一些音频和文字格式 放在文件夹media 三.编写代码 import sys, os sys.path.append(os.getcwd()) # coding:UTF-8 import pygame,sys import os from pygame.locals import* import time pygame.init() # 设置一个长为1250,宽为…

03.配置监控一台服务器主机

配置监控一台服务器主机 安装zabbix-agent rpm -ivh https://mirror.tuna.tsinghua.edu.cn/zabbix/zabbix/4.0/rhel/7/x86_64/zabbix-agent-4.0.11-1.el7.x86_64.rpm配置zabbix-agent,配置的IP地址是zabbix-server的地址,因为要监控这台主机 vim /etc/zabbix/zab…

淘宝线上扭蛋机小程序:推动扭蛋机销量

扭蛋机作为一个新兴的娱乐消费模式,能够带给消费者“盲盒式”的消费乐趣,正在快速发展中。消费者通过投币、扫码支付等,在机器上扭下按钮就可以随机获得一个扭蛋商品,这些商品也包括动漫衍生周边、IP主题商品等,种类多…

先电2.4的openstack搭建

先电2.4版本的openstack,前期虚拟机部署参考上一篇2.2版本,基本步骤是一样的,准备两个镜像文件CentOS-7.5-x86_64-DVD-1804.iso,XianDian-IaaS-V2.4.iso [rootcontroller ~]# cat /etc/sysconfig/network-scripts/ifcfg-eno16777…