【3-神经网络八股】北京大学TensorFlow2.0

news2025/1/18 7:36:16

课程地址:【北京大学】Tensorflow2.0_哔哩哔哩_bilibili

Python3.7和TensorFlow2.1

六讲:

  1. 神经网络计算:神经网络的计算过程,搭建第一个神经网络模型

  1. 神经网络优化:神经网络的优化方法,掌握学习率、激活函数、损失函数和正则化的使用,用Python语言写出SGD、Momentum、Adagrad、RMSProp、Adam五种反向传播优化器

  1. 神经网络八股:神经网络搭建八股,六步法写出手写数字识别训练模型

  1. 网络八股扩展:神经网络八股扩展,增加自制数据集、数据增强、断点续训、参数提取和acc/loss可视化,实现给图识物的应用程序

  1. 卷积神经网络:用基础CNN、LeNet、AlexNet、VGGNet、InceptionNet和ResNet实现图像识别

  1. 循环神经网络:用基础RNN、LSTM、GRU实现股票预测


前两讲:使用TensorFlow2的原生代码搭建神经网络

本讲:使用Keras搭建神经网络(八股:六步法,有Sequential和class两种)


神经网络搭建八股

用TensorFlow API:tf.keras搭建网络八股

keras介绍

tf.keras是TensorFlow2引入的高封装度框架,可以用于快速搭建神经网络模型

官方文档: https://tensorflow.google.cn/api_docs/python/tf

两种学习API的方法:

  1. 在PyCharm集成开发环境中查看框架源码:将鼠标放置在函数上按住Ctrl键会显示函数的基本信息,包括封装函数的类、函数入口参数、函数功能等

  1. 在TensorFlow官网中查询函数文档:通过左边的检索寻找目标函数。以查询 model.fit() 函数为例,打开tf.keras中的Model类,右方目录列出了Model类所包含的函数,点击fit()函数可以看到对于函数的介绍,包括输入参数具体介绍、函数功能等

六步法

  1. import 相关模块

  1. 指定输入网络的训练集和测试集train test,如指定训练集的输入 x_train 和标签 y_train,以及测试集的输入x_test 和标签 y_test

  1. 逐层搭建网络结构,相当于走了一遍前向传播 models.Sequential

model = tf.keras.models.Sequential()

这里还有另一种方法:class MyModel

class MyModel(Model):   
    def __init__(self):   
        super(MyModel, self).__init__()   
        初始化网络结构,搭建出神经网络所需的各种网络结构块
    def call(self, x):   
        调用网络结构块,实现前向传播
        return y
model = MyModel()
  1. 配置训练方法,选择训练时使用的优化器、损失函数和评价指标 model.compile

model.compile()
  1. 执行训练过程,告知训练集和测试集的输入值和标签、每个batch的大小batch_size、数据集的迭代次数epoch model.fit

model.fit()
  1. 打印网络结构,统计参数数目 model.summary

model.summary()

函数用法

Sequential()

Sequential函数是一个容器,描述了神经网络的网络结构,在Sequential函数的输入参数中描述从输入层到输出层的网络结构

model = tf.keras.models.Sequential([网络结构])    # 描述各层网络

网络结构举例:

  • 拉直层:可以变换张量的尺寸,把输入特征拉直为一维数组,只是形状转换,不含计算参数

tf.keras.layers.Flatten()
  • 全连接层:又叫Dense层

tf.keras.layers.Dense(神经元个数,
                      activation="激活函数",   # 字符串给出,可选relu、softmax、sigmoid、tanh等
                      kernel_regularizer="正则化方式")   # 可选tf.keras.regularizers.l1()、tf.keras.regularizers.l2()
  • 卷积层:卷积神经网络

tf.keras.layers.Conv2D(filters=卷积核个数,
                       kernel_size=卷积核尺寸,
                       strides=卷积步长,
                       padding= "valid" or "same")
  • LSTM层:循环神经网络

tf.keras.layers.LSTM()

compile()

用于配置神经网络的训练方法,告知训练时使用的优化器、损失函数和准确率评测标准

model.compile(optimizer=优化器,
              loss=损失函数,
              metrics=["准确率"])

(1)optimizer可以是字符串形式给出的优化器名字,也可以是函数形式(可以设置学习率、动量等超参数)。建议入门时,先使用左边字符串形式的优化器名字,等掌握了整个框架后,可通过TensorFlow官网查询这些函数的具体用法,调节超参数

https://tensorflow.google.cn/api_docs/python/tf/keras/optimizers/experimental/SGD
‘sgd’ or tf.keras.optimizers.SGD(lr=学习率,
                                 decay=学习率衰减率,
                                 momentum=动量参数)

‘adagrad’ or tf.keras.optimizers.Adagrad(lr=学习率,
                                         decay=学习率衰减率)

‘adadelta’ or tf.keras.optimizers.Adadelta(lr=学习率,
                                           decay=学习率衰减率)

‘adam’ or tf.keras.optimizers.Adam (lr=学习率,
                                    beta_1=0.9,
                                    beta_2=0.999)

(2)loss可以是字符串形式给出的损失函数的名字,也可以是函数形式

‘mse’ or tf.keras.losses.MeanSquaredError()
‘sparse_categorical_crossentropy' or tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)   # from_logits 是否是原始输出,即未经过概率分布的输出

损失函数常需要经过softmax函数将输出转化为概率分布的形式。from_logits用来标注该损失函数是否需要转换为概率的形式,取False表示转化为概率分布,取True表示没有转化为概率分布,直接输出

(3)metrics标注网络评测指标

# y_是标签,y是网络输出结果
‘accuracy’ :y_和y都是数值,如y_=[1] y=[1] 
‘categorical_accuracy’ :y_和y都是独热码(概率分布),如y_=[0,1,0] y=[0.256,0.695,0.048]
‘sparse_categorical_accuracy’ :y_是数值,y是概率分布,如y_=[1] y=[0.256,0.695,0.048]

fit()

执行训练过程

model.fit (训练集的输入特征, 
           训练集的标签, 
           batch_size= ,    # 每次喂入神经网络的样本数
           epochs= ,     # 要迭代多少次数据集
           # 以下函数 validation_data 和 validation_split 二选一
           validation_data=(测试集的输入特征,测试集的标签),
           validation_split=从训练集划分多少比例给测试集,
           validation_freq = 多少次epoch测试一次)

summary()

用于打印网络结构和参数统计

model.summary()

对于一个输入为4输出为3的全连接网络,共有15个参数(12个w+3个b)


iris分类代码复现

(一)Sequential搭建

# 1-import
import tensorflow as tf
from sklearn import datasets
import numpy as np

# 2-train test
'''
测试集的输入特征x_test 和 标签y_test 可以像x_train和y_train一样直接从数据集获取
也可以在fit中按比例从训练集中划分(本代码采用这种方式,所以只需加载x_train和y_train即可)
'''
x_train = datasets.load_iris().data
y_train = datasets.load_iris().target
# 以下代码实现了数据集的乱序
np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

# 3-models.Sequential   逐层搭建网络结构
'''
单层全连接神经网络,三个参数分别为:
神经元个数;网络所使用的激活函数;正则化方法
'''
model = tf.keras.models.Sequential([
        tf.keras.layers.Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())
])

# 4-model.compile   配置训练方法
model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),   # SGD优化器,学习率设置为0.1
              # 由于神经网络输出使用了softmax激活函数,使得输出是概率分布,而不是原始输出,故from_logits=False
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              # iris数据集的标签是0/1/2这样的数值,而网络前向传播输出为概率分布
              metrics=['sparse_categorical_accuracy'])

# model.fit   执行训练过程
model.fit(x_train,   # 训练集输入特征
          y_train,   # 训练集标签
          batch_size=32,    # 训练时一次喂入神经网络多少组数据
          epochs=500,    # 数据集迭代循环多少次
          validation_split=0.2,    # 从训练集中选择20%的数据作为测试集
          validation_freq=20)   # 每迭代20次训练集要在测试集中验证一次准确率

# model.summary  打印网络结构,统计参数数目
model.summary()

iris数据集输入是4个特征,是三分类问题

这里test是用 validation_split=0.2 划分的(法1:在model.fit中按比例从训练集中划分就不用写x_test和y_test了)

法2:也可以像x_train和y_train一样直接给定

以上是测试集test的两种划分方式

(二)类class搭建

使用Sequential可以快速搭建出上层输出就是下层输入的顺序网络结构,但如果网络包含跳连(Skip Connection)等复杂非顺序网络结构,Sequential就无法表示了,需要使用class类封装网络结构

class模板

class MyModel(Model):   # MyModel为声明的神经网络的名字,括号中的Model表示创建的类需要继承TensorFlow库中的Model类
# 类中需要定义两个函数
    def __init__(self):   #类的构造函数,用于初始化类的参数
        super(MyModel, self).__init__()   # 初始化父类的参数
        初始化网络结构,搭建出神经网络所需的各种网络结构块
    def call(self, x):   #调用__init__()函数完成初始化的网络块,实现前向传播并返回推理值
        调用网络结构块,实现前向传播
        return y

model = MyModel()

可以认为 __init__() 定义所需网络结构块,准备出搭建网络所需的各种积木,call()函数调用 __init__() 中搭建好的积木,实现前向传播

使用class方式搭建iris网络结构

class IrisModel(Model):
    def __init__(self):
        super(IrisModel, self).__init__()
        # 在__init__函数中定义了要在call函数中调用的具有三个神经元的全连接网络Dense
        self.d1 = Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())   # d1是给这一层起的名字,每一层都用self.引导

    def call(self, x):
        y = self.d1(x)    # 在call函数中调用self.d1实现了从输入x输出y的前向传播
        return y

搭建好网络结构后,只需要使用 Model = MyModel() 构建类的对象,就可以使用该模型了

model = IrisModel()   # 实例化

完整代码

这里我遇到了两个报错:

  1. cannot import name ‘dtensor‘ from ‘tensorflow.compat.v2.experimental‘

原因:keras版本太高,需要降低到和TensorFlow版本一致,我这里TensorFlow是2.8.0,keras降到2.8.0即可

报错:cannot import name ‘dtensor‘ from ‘tensorflow.compat.v2.experimental‘ (/Users/pxs/anaconda3/lib - CSDN博客
  1. cannot find reference ‘keras’ in ‘__init__.py‘

这两行代码下一直有红波浪线,但代码可以运行

原因:PyCharm找不到引用,改为下面代码即可

from keras.layers import Dense   # Dense层
from keras import Model   # Model模块
cannot find reference ‘keras’ in ‘__init__.py‘ - CSDN博客

解决上面两个问题后,完整代码如下:

# 和Sequential方法相比,改动的地方用##数字##标注出
import tensorflow as tf
from keras.layers import Dense  ##1##
from keras import Model  ##2##
from sklearn import datasets
import numpy as np

x_train = datasets.load_iris().data
y_train = datasets.load_iris().target

np.random.seed(116)
np.random.shuffle(x_train)
np.random.seed(116)
np.random.shuffle(y_train)
tf.random.set_seed(116)

##3##
class IrisModel(Model):
    def __init__(self):
        super(IrisModel, self).__init__()
        self.d1 = Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2())   # 在__init__函数中定义了要在call函数中调用的具有三个神经元的全连接网络Dense

    def call(self, x):
        y = self.d1(x)    # 在call函数中调用self.d1实现了从输入x输出y的前向传播
        return y
##4##
model = IrisModel()   # 实例化

model.compile(optimizer=tf.keras.optimizers.SGD(lr=0.1),
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=500, validation_split=0.2, validation_freq=20)

model.summary()

MNIST手写数字识别数据集

数据集介绍

一共7万张图片,是28×28=784个像素的0-9手写数字数据集,其中6万张用于训练,1万张用于测试

import tensorflow as tf
from matplotlib import pyplot as plt

# 导入数据集
mnist = tf.keras.datasets.mnist   # keras函数库提供了使用mnist数据集的接口
(x_train, y_train), (x_test, y_test) = mnist.load_data()   # load_data()直接从mnist中读取测试集和训练集

# 可视化训练集输入特征的第一个元素
plt.imshow(x_train[0], cmap='gray')  # 绘制灰度图
plt.show()
# 打印出训练集输入特征的第一个元素
print("x_train[0]:\n", x_train[0])    # 28行28列个像素值的二维数组(0表示纯黑色,255表示纯白色)
# 打印出训练集标签的第一个元素
print("y_train[0]:\n", y_train[0])    # 数值5
# 打印出整个训练集输入特征形状
print("x_train.shape:\n", x_train.shape)   # 6万个28行28列的数据
# 打印出整个训练集标签的形状
print("y_train.shape:\n", y_train.shape)   # 6万个标签
# 打印出整个测试集输入特征的形状
print("x_test.shape:\n", x_test.shape)   # 1万个28行28列的三维数据
# 打印出整个测试集标签的形状
print("y_test.shape:\n", y_test.shape)   # 1万个标签

训练MNIST数据集

使用全连接网络,将784个像素点(灰度值)组成的长度为784的一维数组作为输入特征

输入全连接网络时需要先将数据拉直为 一维数组 tf.keras.layers.Flatten()

训练时需要将输入特征的灰度值归一化到 [0,1] 区间,这可以使网络更快收敛

(一)Sequential搭建

# 用Sequential实现手写数字识别训练

# 1-import
import tensorflow as tf

# 2-train test
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0    # 对输入网络的输入特征进行归一化,使原本0-255之间的灰度值变成0-1之间的数值
# 把输入特征的数值变小更适合神经网络吸收

# 用Sequential搭建网络   3-models.Sequential
model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(),   # 先把输入特征拉直为一维数组,即748个数值
        tf.keras.layers.Dense(128, activation='relu'),    # 定义第一层网络有128个神经元
        tf.keras.layers.Dense(10, activation='softmax')    # 定义第二层网络有10个神经元,使输出符合概率分布
])

# 用compile配置训练方法   4-model.compile
model.compile(optimizer='adam',   # 优化器
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),   # 损失函数
              # 由于第二层网络用了softmax让输出符合概率分布了,不是直接输出的,所以from_logits=False
              # 如果输出不满足概率分布,要=True
              metrics=['sparse_categorical_accuracy'])  # 数据集中的标签是数值,神经网络输出y是概率分布

# 在fit中执行训练过程   5-model.fit
model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)   # validation_freq=1 每迭代一次训练集,执行一次测试集的评测

# 打印出网络结构和参数统计   6-model.summary
model.summary()
  1. MNIST数据集有60000张图片用来训练,batch_size=32,所以每轮要迭代60000/32=1875次,共迭代5轮,即5 epochs

  1. 训练时每个step给出的是训练集accuracy,不具有参考价值。有实际评判价值的是validation_freq中设置的、隔若干轮输出的测试集accuracy

(二)类class搭建

# 用类实现手写数字识别训练
# 和Sequential方法相比,只是实例化model的方法不同
import tensorflow as tf
from keras.layers import Dense, Flatten
from keras import Model

mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

class MnistModel(Model):
    def __init__(self):   # 定义call函数中所用到的层
        super(MnistModel, self).__init__()
        self.flatten = Flatten()
        self.d1 = Dense(128, activation='relu')
        self.d2 = Dense(10, activation='softmax')

    def call(self, x):   # 从输入x到输出y,走过一次前向传播返回输出y
        x = self.flatten(x)
        x = self.d1(x)
        y = self.d2(x)
        return y

model = MnistModel()   # 实例化model

model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
              metrics=['sparse_categorical_accuracy'])

model.fit(x_train, y_train, batch_size=32, epochs=5, validation_data=(x_test, y_test), validation_freq=1)

model.summary()

FASHION衣裤识别数据集及训练

Fashion_mnist数据集与MNIST数据集几乎一样,包括6w张训练图片和1w张测试图片,图片被分为十类(如T恤、裤子、套头衫等等),每张图片为28×28的分辨率(像素点的灰度值数据)

训练衣服、裤子等图片的识别模型:与训练MNIST数据集的不同之处就是加载数据集的代码不同,在此不再赘述

fashion = tf.keras.datasets.fashion_mnist
(x_train, y_train),(x_test, y_test) = fashion.load_data()   # 可以使用.load_data()直接从fashion数据集中读取训练集和测试集

最后准确率为86.85%

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

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

相关文章

走进 HTML

文章目录01 什么是HTML?02 HTML的基本结构03 网页基本标签04 图像标签05 链接标签06 块元素和行内元素07 列表07 表格08 视频和音频09 页面结构10 iframe内联框架11 表单语法👉 表单元素格式👉 表单的应用👉 表单初级验证01 什么是…

【Mysql】 数据库用户管理

【Mysql】 数据库用户管理 DCL:英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。 1. 管理用户 想要对数据库用户进行操作,我们首先得进入 mysql 数据库 use mysql1.1 查询用户 select * from user;该条命…

每日一问-ChapGPT-20230122-关于春节习俗

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230116-关于春节习俗世界有哪些国家过春节中国各个地区过春节都有哪些习俗台湾的春节习俗有哪些新加坡过春节有哪些习俗初一到初七的传统习俗有哪些在热闹的节日里,自己无法融入氛围,是什么原因不喜…

【MySQL】第六部分 单行函数

【MySQL】第六部分 单行函数 文章目录【MySQL】第六部分 单行函数6. 单行函数6.1 常用的函数6.2 角度与弧度转换函数6.3 三角函数6.4 指数与对数6.5 进制转换6.6 字符串函数6.7 日期时间函数6.7.1 获取时间和日期6.7.2 日期与时间戳的转换6.7.3 获取月份、星期、星期数、天数等…

筑基二层 —— 图解函数递归、数组详解

目录 一.修炼必备 二.图解递归的执行过程 三.数组 3.1 一维数组 3.2 二维数组 3.3 数组的共同问题 一.修炼必备 1.入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com) 2.趁手武…

I.MX6ULL裸机开发笔记7:汇编点亮LED灯

一、vscode调整 加入汇编插件ARM vscode权限受限(因为Ubuntu中的文件有的是root权限创建的,vscode以普通用户打开的话没有操作权限)chmod 修改文件或者文件夹权限 二、编程步骤 使能GPIO时钟设置引脚复用位GPIO设置引脚属性(上下…

深入跨域问题(3) - 利用 JSONP 解决跨域

目录 1.简单例子: 2.基本实现 3.JSONP 与 CORS 的对比 什么是跨域,在这篇文章内部就不再讲述了,本文主要着重于实现 JSONP 。 script 标签: 根据同源策略的限制,在 端口,域名,协议 这三者 …

AX7A200教程(3): DDR3突发读写

上一个章节我们新建工程,然后进行基本的初始化操作,这个章节将在上个工程的基础上进行突发读写因ddr3读写部分控制信号比较多,所以ddr3读写控制模块比较复杂,本章节着重于一个256位数据的突发读写,ddr读写控制模块暂不…

【JavaWeb】前端开发三剑客之CSS(下)

✨哈喽,进来的小伙伴们,你们好耶!✨ 🛰️🛰️系列专栏:【JavaWeb】 ✈️✈️本篇内容:CSS的深度学习! 🚀🚀代码托管平台github:JavaWeb代码存放仓库! ⛵⛵作者…

(考研湖科大教书匠计算机网络)第二章物理层-第一、二节:物理层基本概念和传输媒体

文章目录一:物理层概念二:物理层传输媒体(1)导引型传输媒体A:同轴电缆B: 双绞线C:光纤①:光纤通信②:光纤D:电力线(2)非导引型传输媒体…

day22 多线程02

1.线程池 1.1 线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定义在了java.lang.Thread.State…

[ESP][驱动]ST7701S RGB屏幕驱动

ST7701SForESP ST7701S ESP系列驱动,基于ESP-IDF5.0,ESP32S3编写。 本库只负责SPI的配置,SPI设置屏幕两方面。由于RGB库和图形库的配置无法解耦,具体使用的RGB库,图形库需要自行配置添加。 SPI的指令,地…

操作系统真相还原_第5章第2节:内存分页机制

文章目录分段机制分页机制一级页表二级页表启用分页机制的过程启用分页机制(二级页表)详解程序include.incmbr.sloader.s写入硬盘启动bochs执行分段机制 分页机制 一级页表 二级页表 启用分页机制的过程 1、准备好页目录项及页表 2、将页表地址写入控制寄存器cr3 3、寄存器cr0…

【ArcGIS微课1000例】0059:三种底图影像调色技巧案例教程

三种调整影像底图效果的技术,让你的图纸清新脱俗,做出的图更美观! 文章目录 方法一:影像源类型调整方法二:符号拉伸类型设置方法三:影像分析模块设置方法一:影像源类型调整 这种方法是最基础、最简单的一种方法,可以调整的内容有限。当大家发现导入了影像,然后影像图…

Spring Batch 基本概念和运行示例

基本概念 Spring Batch是批处理框架。 作业(Job)是状态以及状态之间转换的集合。 作业里包含步骤(Spring Bean),每一个步骤解耦到独立的处理器中,并负责自己的数据,把所需的业务逻辑应用到数据上,然后把数…

【React】组件的创建与事件绑定

📘前言 🚩🚩🚩 💎个人主页: 阿选不出来 💨💨💨 💎个人简介: 一名大二在校生,学习方向前端,不定时更新自己学习道路上的一些笔记. 💨💨&#x1f4a…

C规范编辑笔记(十三)

往期文章: C规范编辑笔记(一) C规范编辑笔记(二) C规范编辑笔记(三) C规范编辑笔记(四) C规范编辑笔记(五) C规范编辑笔记(六) C规范编辑笔记(七) C规范编辑笔记(八) C规范编辑笔记(九) C规则编辑笔记(十) C规范编辑笔记(十一) C规范编辑笔记(十二) 正文&#xff…

CSS背景:背景色/背景图像/背景重复/背景附着/简写背景属性(一文搞懂)

目录 CSS背景 CSS 背景色 实例 其他元素 实例 不透明度 / 透明度 实例 使用 RGBA 的透明度 实例 CSS 背景图像 实例 实例 实例 CSS 背景重复 实例 实例 CSS background-repeat: no-repeat 实例 CSS background-position 实例 CSS 背景附着 实例 实例 C…

linux系统中利用QT实现视频监控的基本方法

大家好,今天主要和大家分享一下,如何利用QT实现视频监控的基本操作。 目录 第一:视频监控基本简介 第二:实验流程图 第三:视频监控之服务器 第四:视频监控之客户端实现 第一:视频监控基本简…

Java基础之《netty(30)—RPC调用流程分析》

一、RPC基本介绍 1、RPC(Remote Procedure Call)—远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序无需额外的为这个交互作用编程。 2、两个或多个应用程序都分布在不同的…