图神经网络代码学习—基本使用与分类任务

初步接触图神经网络代码

环境配置

对于在多目标跟踪中应用图匹配网络,需要学习使用GNN图神经网络,对于图神经网络的实现需要学习使用一下库和项目来进行实践。

PyG(PyTorch Geometric)是一个建立在 PyTorch 基础上的库,用于轻松编写和训练图神经网络(GNN),用于与结构化数据相关的广泛应用。

官网地址:https://www.pyg.org/
github地址:https://github.com/pyg-team/pytorch_geometric

在这里插入图片描述

这里自己之前学习GMtracker的时候曾经踩过坑,按照这个图神经网络相关的库,用pip直接去安装的话大概率是安装失败的。需要去手动的进行下载和使用。

在安装的时候为了使用全部的功能和相关的一些扩展库,需要尽量的去使用PYG的编译后的版本同时也需要去安装GMtracker中使用过的额外的一些拓展库。

在这里插入图片描述

在这里插入图片描述

这里为了以后的方便我们提供一下安装的相关地址:https://data.pyg.org/whl/

在里面找到对应的版本号,cu是我们的GPU的 cpu是cpu版的torch,同时我们要学着cpxx(指的是pytorch的版本)

额外拓展包主要就包括了下面的四个扩展包

pip install torch_scatter-2.0.5-cp38-cp38-win_amd64.whl
pip install torch_sparse-0.6.7-cp38-cp38-win_amd64.whl
pip install torch_cluster-1.5.7-cp38-cp38-win_amd64.whl
pip install torch_spline_conv-1.2.0-cp38-cp38-win_amd64.whl

装完这些所有的依赖之后我们在执行pip install torch_geometric即可安装完成所有的图神经网络相关的环境信息。

图神经网络的基本使用

  • Graph Neural Networks
  1. 致力于解决不规则数据结构(图像和文本相对格式都固定,但是社交网络与化学分子等格式肯定不是固定的)

  2. GNN模型迭代更新主要基于图中每个节点及其邻居的信息,基本表示如下:

x v ( ℓ + 1 ) = f θ ( ℓ + 1 ) ( x v ( ℓ ) , { x w ( ℓ ) : w ∈ N ( v ) } ) \mathbf{x}_{v}^{(\ell+1)}=f_{\theta}^{(\ell+1)}\left(\mathbf{x}_{v}^{(\ell)},\left\{\mathbf{x}_{w}^{(\ell)}: w \in \mathcal{N}(v)\right\}\right) xv(+1)=fθ(+1)(xv(),{xw():wN(v)})

使用的数据集介绍:Zachary’s karate club network.

该图描述了一个空手道俱乐部会员的社交关系,以34名会员作为节点,如果两位会员在俱乐部之外仍保持社交关系,则在节点间增加一条边。 每个节点具有一个34维的特征向量,一共有78条边。 在收集数据的过程中,管理人员 John A 和 教练 Mr. Hi(化名)之间产生了冲突,会员们选择了站队,一半会员跟随 Mr. Hi 成立了新俱乐部,剩下一半会员找了新教练或退出了俱乐部。

在这里插入图片描述

我们学习的尝试就是:通过使用官方提供的API来学习各种图神经网络的应用,对于图神经网络有一定的了解,为之后用GNN进行图匹配铺垫。

可以直接参考其API:https://pytorch-geometric.readthedocs.io/en/latest/modules/datasets.html#torch_geometric.datasets.KarateClub

节点与边的结构

我们使用这个数据集的任务是对这个一个图上的点来做分类任务。

from torch_geometric.datasets import KarateClub
dataset = KarateClub()

print(len(dataset)) # 只有一个图
print(dataset.num_features) # 每一个点有34个特征
print(dataset.num_classes) # 对于每一个点做一个二分类的任务

print(dataset[0]) # 当前只有一个图打印这个图的一个结果

1
34
2
Data(edge_index=[2, 156], x=[34, 34], y=[34])

Data(edge_index=[2, 156], x=[34, 34], y=[34]):对其输出的一个数据结构我们进行简单的解释和说明:

X=[F x M] : F=34代表我们有34个节点,M=34每一个节点有34个特征。后面的edge[2,156]代表的是有156个相连的边。y代表的是标签。应该是通过标签来计算损失的。

data = dataset[0]
edge_index = data.edge_index
print(edge_index.t()) # 转置成ex2的形状来进行输出

使用networkx可视化展示

这里是可视化图得到的一个结果,也就是将其分成了两类。

在这里插入图片描述

def visualize_graph(G, color):
    plt.figure(figsize=(7,7))
    plt.xticks([])
    plt.yticks([])
    nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), with_labels=False,
                     node_color=color, cmap="Set2")
    plt.show()
from torch_geometric.utils import to_networkx

G = to_networkx(data, to_undirected=True)
visualize_graph(G, color=data.y)

图神经网络的定义

Graph Neural Networks 网络定义:GCN layer (Kipf et al. (2017)) 定义如下:https://arxiv.org/pdf/1609.02907

x v ( ℓ + 1 ) = W ( ℓ + 1 ) ∑ w ∈ N ( v ) ∪ { v } 1 c w , v ⋅ x w ( ℓ ) \mathbf{x}_{v}^{(\ell+1)}=\mathbf{W}^{(\ell+1)} \sum_{w \in \mathcal{N}(v) \cup\{v\}} \frac{1}{c_{w, v}} \cdot \mathbf{x}_{w}^{(\ell)} xv(+1)=W(+1)wN(v){v}cw,v1xw()

下面我们给出图神经网络的参考文档:https://pytorch-geometric.readthedocs.io/en/latest/modules/nn.html#torch_geometric.nn.conv.GCNConv

下面我们编写代码学习一下图神经网络的网络结构是如何进行定义的:

# 导入全连接层和一个图卷积层
import torch
from torch.nn import Linear
from torch_geometric.nn import GCNConv
from torch_geometric.datasets import KarateClub
dataset = KarateClub()

class GCN(torch.nn.Module):
    def __init__(self):
        super().__init__()
        torch.manual_seed(1234)
        self.conv1 = GCNConv(dataset.num_features,4) # 只需要定义好输入特征和输出特征即可
        self.conv2 = GCNConv(4,4)
        self.conv3 = GCNConv(4,2)
        self.classifier = Linear(2,dataset.num_features) # 最后一层的分类结构

    def forward(self, x, edge_index):
        h = self.conv1(x,edge_index) # 输入特征与邻接矩阵
        h = h.tanh()
        h = self.conv2(h,edge_index)
        h = h.tanh()
        h = self.conv3(h,edge_index)
        h = h.tanh()

        # 分类层
        out = self.classifier(h)

        # 返回最后的结果

        return out,h

model = GCN()
print(model)
GCN(
  (conv1): GCNConv(34, 4)
  (conv2): GCNConv(4, 4)
  (conv3): GCNConv(4, 2)
  (classifier): Linear(in_features=2, out_features=34, bias=True)
)

输出特征展示

最后不是输出了两维特征嘛,画出来看看长啥样

但是,模型还木有开始训练,训练完成之后会在展示训练完成之后的样子。

在这里插入图片描述

模型训练

这里的图神经网络的训练是一个半监督的任务。通过半监督的方法来进行执行。

import time

import torch.nn

import GCN2
model = GCN2.GCN()

criterion =  torch.nn.CrossEntropyLoss() # 定义交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定义Adam优化器

def train(data):
    optimizer.zero_grad()
    out,h = model(data.x,data.edge_index) # h是两维度的向量便于画图
    # print(h)
    loss = criterion(out,data.y)
    loss.backward()
    optimizer.step()
    return loss ,h

from torch_geometric.datasets import KarateClub
dataset = KarateClub()
data = dataset[0]

for epoch in range(401):
    loss,h = train(data)
    if epoch % 10 == 0:
        GCN2.visualize_embedding(h,color=data.y,epoch=epoch,loss=loss)
        time.sleep(0.3)

在这里插入图片描述

图神经网络点的分类任务

Cora dataset(数据集描述:Yang et al. (2016)):论文引用数据集,每一个点有1433维向量最终要对每个点进行7分类任务(每个类别只有20个点有标注)

Cora数据集是由McCallum等人于2000年创建的,用于研究文本分类和信息检索它在2008年由Sen等人进行了更新,增加了更多的文档和类别Cora数据集包含2708篇科学论文,这些论文被分类到7个不同的领域。每个论文都由一个1433维的二进制词袋模型特征向量表示,其中每个维度对应一个词汇,1表示词汇在论文中出现,0表示未出现。此外,每篇论文至少引用了一篇其他论文,或者被其他论文引用,形成了一个连通的图结构,没有任何孤立点。

  • Cora数据集的具体特点如下:样本特征与标签:Cora数据集包含2708个样本点,每个样本点都是一篇科学论文,被分为7个类别,分别是神经网络、强化学习、规则学习、概率方法、遗传算法、理论研究、案例相关。每篇论文都由一个1433维的词向量表示,词向量的每个元素对应一个词,取值只有0或1。
  • 邻接矩阵:Cora数据集的论文之间存在引用关系,这些引用关系构成了图的边。数据集中共有5429条边,表示论文之间的引用链接。
  • 文件格式:Cora数据集包含三个文件,分别是cora.cites、cora.content和README。cora.content包含了所有论文的独立信息,包括论文的编号、词向量和类别标签;cora.cites包含了论文之间的引用记录。
  • Cora数据集因其结构化的特点和丰富的引用关系,被广泛用于图神经网络(GNN)和半监督学习领域的研究,特别是在图卷积网络(GCN)的研究中,Cora数据集被用作基准数据集。它不仅用于节点分类任务,还涉及到图结构数据的特征提取和模式发掘,满足聚类、分类、预测等多种图学习任务的需求.

这里的学习和传统的CNN的步骤相同,我们对上面介绍的数据集进行一个分类的任务。这里加入一个对比实验与传统的MLP进行分类进行对比。

数据集下载

from torch_geometric.datasets import Planetoid #下载数据集用的
from torch_geometric.transforms import NormalizeFeatures

dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())#transform预处理

print()
print(f'Dataset: {dataset}:')
print('======================')
print(f'Number of graphs: {len(dataset)}')
print(f'Number of features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')

data = dataset[0]  # Get the first graph object.

print()
print(data)
print('===========================================================================================================')

# Gather some statistics about the graph.
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges: {data.num_edges}')
print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}')
print(f'Number of training nodes: {data.train_mask.sum()}')
print(f'Training node label rate: {int(data.train_mask.sum()) / data.num_nodes:.2f}')
# print(f'Has isolated nodes: {data.has_isolated_nodes()}')
# print(f'Has self-loops: {data.has_self_loops()}')
print(f'Is undirected: {data.is_undirected()}')

这里会产生报错,我们参考下面的解决方案解决:https://blog.csdn.net/takedachia/article/details/140211194

下面是打印出来的数据集的基本结构。2708个节点 10556个边。等等。

Dataset: Cora():
======================
Number of graphs: 1
Number of features: 1433
Number of classes: 7

Data(edge_index=[2, 10556], test_mask=[2708], train_mask=[2708], val_mask=[2708], x=[2708, 1433], y=[2708])
===========================================================================================================
Number of nodes: 2708
Number of edges: 10556
Average node degree: 3.90
Number of training nodes: 140
Training node label rate: 0.05
Is undirected: True

在这里插入图片描述

编写可视化相关的函数。

import matplotlib.pyplot as plt
from sklearn.manifold import TSNE # 降维的库降到2维画图

def visualize(h, color):
    z = TSNE(n_components=2).fit_transform(h.detach().cpu().numpy())

    plt.figure(figsize=(10,10))
    plt.xticks([])
    plt.yticks([])

    plt.scatter(z[:, 0], z[:, 1], s=70, c=color, cmap="Set2")
    plt.show()

使用MLP进行分类和训练

两层的MLP结构定义:

import torch
from torch.nn import Linear
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid #下载数据集用的
from torch_geometric.transforms import NormalizeFeatures
dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())

class MLP(torch.nn.Module):
    def __init__(self, hidden_channels):
        super().__init__()
        torch.manual_seed(12345)
        self.lin1 = Linear(dataset.num_features, hidden_channels)
        self.lin2 = Linear(hidden_channels, dataset.num_classes)

    def forward(self, x):
        x = self.lin1(x)
        x = x.relu()
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.lin2(x)
        return x

model = MLP(hidden_channels=16)
print(model)
MLP(
  (lin1): Linear(in_features=1433, out_features=16, bias=True)
  (lin2): Linear(in_features=16, out_features=7, bias=True)
)

模型训练函数的编写:

import torch
from torch_geometric.nn import GCNConv

from torch.nn import Linear
import visual
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid #下载数据集用的
from torch_geometric.transforms import NormalizeFeatures

import MLP
dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())
data = dataset[0]



def train():
    model.train()
    optimizer.zero_grad()  # Clear gradients.
    out = model(data.x)  # Perform a single forward pass.
    loss = criterion(out[data.train_mask], data.y[data.train_mask])  # Compute the loss solely based on the training nodes.
    loss.backward()  # Derive gradients.
    optimizer.step()  # Update parameters based on gradients.
    return loss

def tes():
    model.eval()
    out = model(data.x)
    pred = out.argmax(dim=1)  # Use the class with  probability.
    test_correct = pred[data.test_mask] == data.y[data.test_mask]  # Check against ground-truth labels.
    test_acc = int(test_correct.sum()) / int(data.test_mask.sum())  # Derive ratio of correct predictions.
    return test_acc

model = MLP.MLP(hidden_channels=16)
criterion = torch.nn.CrossEntropyLoss()  # Define loss criterion.
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)  # Define optimizer.


for epoch in range(1, 201):
    loss = train()
    print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')

test_acc = tes()
print(f'Test Accuracy: {test_acc:.4f}')

在这里插入图片描述

使用图神经网络进行分类和训练

图神经网络的结构定义:

from torch_geometric.nn import GCNConv
import torch
from torch.nn import Linear
import visual
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid #下载数据集用的
from torch_geometric.transforms import NormalizeFeatures
dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())
data = dataset[0]
class GCN(torch.nn.Module):
    def __init__(self, hidden_channels):
        super().__init__()
        torch.manual_seed(1234567)
        self.conv1 = GCNConv(dataset.num_features, hidden_channels)
        self.conv2 = GCNConv(hidden_channels, dataset.num_classes)

    def forward(self, x, edge_index):
        x = self.conv1(x, edge_index)
        x = x.relu()
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.conv2(x, edge_index)
        return x

model = GCN(hidden_channels=16)
print(model)

model.eval()

out = model(data.x, data.edge_index)
visual.visualize(out, color=data.y)

训练与最后的结果可视化

在这里插入图片描述

import torch
from torch_geometric.nn import GCNConv

from torch.nn import Linear
import visual
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid #下载数据集用的
from torch_geometric.transforms import NormalizeFeatures

import GCN_class
dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())
data = dataset[0]



def train():
    model.train()
    optimizer.zero_grad()
    out = model(data.x, data.edge_index)
    loss = criterion(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss

def tes():
    model.eval()
    out = model(data.x,data.edge_index)
    pred = out.argmax(dim=1)  # Use the class with  probability.
    test_correct = pred[data.test_mask] == data.y[data.test_mask]  # Check against ground-truth labels.
    test_acc = int(test_correct.sum()) / int(data.test_mask.sum())  # Derive ratio of correct predictions.
    return test_acc

model = GCN_class.GCN(hidden_channels=16)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
criterion = torch.nn.CrossEntropyLoss()

for epoch in range(1, 101):
    loss = train()
    print(f'Epoch: {epoch:03d}, Loss: {loss:.4f}')

test_acc = tes()
print(f'Test Accuracy: {test_acc:.4f}')

准确率的提升从50%到80%由此可见提升还是非常大的。

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

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

相关文章

操作系统:死锁与饥饿

目录 死锁概念 饥饿与饿死概念 饥饿和死锁对比 死锁类型 死锁条件(Coffman条件) 死锁恢复方法 死锁避免 安全状态与安全进程序列: 银行家算法: 死锁检测时机(了解): 死锁检测 死锁案…

SkyWalking Helm Chart 4.7.0 安装、配置

https://skywalking.apache.org/events/release-apache-skywalking-kubernetes-helm-chart-4.7.0/https://github.com/apache/skywalking-helm/tree/v4.7.0https://skywalking.apache.org/zh/2020-04-19-skywalking-quick-start/简介 skywalking 是分布式系统的 APM(Applicat…

electron 打包 webview 嵌入需要调用电脑摄像头拍摄失败问题

electron 打包 webview 嵌入需要调用电脑摄像头拍摄失败问题 这篇文章是接我cocos专栏的上一篇文章继续写的,我上一篇文章写的是 cocos 开发触摸屏项目,需要嵌入一个网页用来展示,最后通过 electron 打包成 exe 程序,而且网页里面…

嵌入式Linux应用开发中CAN通信实现

14.1 CAN介绍 14.1.1 CAN是什么? CAN,全称为“Controller Area Network”,即控制器局域网,是国际上应用最广泛的现场总线之一。最初,CAN 被设计作为汽车环境中的微控制器通讯,在车载各电子控制装置 ECU 之间交换信息,形成汽车电子控制网络。比如:发动机管理系统、变速…

Grafana功能菜单介绍

Grafana的功能菜单设计为侧边栏(sidebar)形式,可以折叠隐藏,便于我们更加专注数据的可视化。现将菜单栏各项功能进行编号讲解,如下图所示:① Grafana Logo 在这里插入图片描述 点击Grafana的logo,无论当前处于哪个页面,都会跳转回Home Page(主页)。② 新建与导入用于…

MVC基础——市场管理系统(二)

文章目录 项目地址三、Produtcts的CRUD3.1 Products列表的展示页面(Read)3.1.1 给Product的Model里添加Category的属性3.1.2 View视图里展示Product List3.2 增加Product数据(Add)3.2.1 创建ViewModel用来组合多个Model3.2.2 在_ViewImposts里引入ViewModels3.2.3 添加Add的…

前端 mp4 视频改成 m3u8 流模式

前端 mp4 视频改成 m3u8 流模式 mp4 视频的问题 1、mp4 视频通常对应一个文件,播放时需要加载全部文件,消耗网络资源。如果用户从中间某个时间访问,也会从头开始下载,浪费服务器性能。 2、mp4 视频文件容易被用户下载到本地。有…

爬虫基础之代理的基本原理

在做爬虫的过程中经常会遇到一种情况,就是爬虫最初是正常运行、正常抓取数据的,一切看起来都是那么美好,然而一杯茶的工夫就出现了错误,例如 403 Forbidden,这时打开网页一看,可能会看到“您的IP访问频率太…

如何将自己的PHP类库发布到composer仓库

将自己的 PHP 类库发布到 Composer 仓库,需要经过一系列的准备和操作步骤,以下是详细说明: 准备工作 创建类库项目:确保你的 PHP 类库项目具有清晰的目录结构,遵循 PSR-4 等 PHP 编码规范。通常,类文件应…

频道web - 性能优化之往返缓存

性能优化之往返缓存 往返缓存简介:如何验证当前页面是否有往返缓存?有哪些开发场景可以用bfcache提升性能?哪些无需关注?阻止页面进行往返缓存的行为都有哪些?1、缓存2、强制刷新3、浏览器设置4、JavaScript 代码5、网络问题6、 iframe 本身不符合 bfcache 的条件为什么会…

当前热门 DApp 模式解析:六大方向的趋势与创新

去中心化应用(DApp)随着区块链技术的不断发展,已经成为 Web3 领域的核心创新之一。与传统应用不同,DApp 通过智能合约运行在区块链上,具有去中心化、透明、安全等特点。近年来,随着用户需求的变化和技术的发…

Windows中将springboot项目运行到docker的容器中

0,先打包好项目,再启动docker 1,在Java项目根目录下创建一个名为Dockerfile的文件(没有扩展名),并添加以下内容。 # 使用OpenJDK的基础镜像 FROM openjdk:8-jdk-alpine# 设置工作目录 WORKDIR /app# 将项…

使用html 和javascript 实现微信界面功能1

1.功能说明: 搜索模块: 提供一个搜索框,但目前没有实现具体的搜索功能。 好友模块: 在左侧的“好友”部分有一个“查看好友”按钮。点击左侧的“查看好友”按钮时,会在右侧显示所有好友的列表。列表中每个好友可以点击查看详情,包…

uniapp——H5中使用富文本编辑器,如何使用。

一、插件市场 去插件市场找到这个插件https://ext.dcloud.net.cn/plugin?id14726 二、引入 找到自己项目引入 项目里面多了很多文件 三、使用 找到A页面&#xff0c;在里面引入组件 <view class"editBox"><sp-editor exportHtml"handleExpor…

前端视角下的Go语法学习:创建 Go 项目

今日话题 使用 GoLand 创建 Go 项目 作者&#xff1a; 时间&#xff1a;2024年6月20日 17时16分14秒 主线任务 一、GoLand 创建项目 1、点击 “new Project” 按钮 2、已经有下载过两个 Golang SDK 版本&#xff0c;选择版本创建即可~ 3、如果没有下载过Golang SDK&#…

使用pyinstaller打包pyqt的程序,运行后提示ModuleNotFoundError: No module named ‘Ui_main‘

环境&#xff1a;windowpython3.9pyqt6 使用pyqt UI编辑器生成了main.ui &#xff0c;main.ui编译成了Ui_main.py main.py 使用当前目录下的Ui_main.py。 打包过程没报错&#xff0c;运行报错。 错误如下: 解决方法&#xff1a;pyinstaller -Fw main.py --paths. 使…

1. 机器学习基本知识(4)——机器学习测试和验证

1.6 测试和验证 了解模型对新实例的泛化能力的唯一方法是在新实例上进行实际尝试。 一种方法是将模型部署到生产环境并监控其性能。 ​ 这种方法很有效&#xff0c;但如果模型非常糟糕&#xff0c;你的用户就会抱怨&#xff0c;所以这显然不是最好的方法。 更好的选择是将数…

Qwen 论文阅读记录

本文仅作自己初步熟悉大模型&#xff0c;梳理之用&#xff0c;慢慢会更改/增加/删除&#xff0c;部分细节尚未解释&#xff0c;希望不断学习之后&#xff0c;能够完善补充。若有同道之人&#xff0c;欢迎指正探讨。 关于后面的code-qwen and math-qwen&#xff0c;我个人认为依…

yarn 安装问题

Couldn’t find package “regenerator-runtime” on the “npm” registry. Error: Couldn’t find package “watch-size” on the “npm” regist 标题Error: Couldn’t find package “babel-helper-vue-jsx-merge-props” on the “npm” registry. Error: Couldn’t f…

【开源】基于SpringBoot框架的音乐网站与分享平台(计算机毕业设计)+万字说明文档 T011

系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境&#xff1a; Tomcat 7.x,8.x,9.x版本均可 操作系统…