深度主动学习(Deep Active Learning)——基于pytorch和ALipy工具包实现双向GRU模型

news2024/10/7 17:26:00

前言

在ALipy的官网说ALipy只支持sklearn和tensorflow模型,模型对象应符合 scikit-learn api。
但是alipy提供了ToolBox的工具箱,里面包装了多种查询策略,计算指标等工具,几乎具有Alipy的全部功能,虽然不能使用ALipy提供的AlExperiment直接加载pytorch模型进行训练,但是可以使用ALipy中提供的ToolBox调用查询策略,计算指标等包装类。
我们的主动学习模型,使用ToolBox结合pytorch模型,可以省去写查询策略、计算指标等的代码。

流程

在这里插入图片描述
数据集分为训练集和测试集,数据集里的实例都是有标签值的,都是被标记的数据。
在训练集中将一部分数据(如:0.1,initial_label_rate = 0.1)作为已标记的数据,假定剩下的数据都是没有标记的(其实是被标记的),更具查询策略从假定的未标记的数据集中选出若干个实例(query_batch_size = 10 # 查询策略每次查询的实例数),加入到已标记的数据集,对模型进行训练。重复若干次(num_of_queries = 50 # 如果停止策略是num_of_queries,则设置查询次数)。

在这里插入图片描述
将训练集划分若干次(split_count = 20 # 将训练集划分出多少个初始化被标记集合)
注意:已标记数据集i+假定的未标记数据集i=训练集

数据集

数据集下载地址

代码

import copy
from sklearn.datasets import make_classification
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
import numpy as np
import pandas as pd
import time
import matplotlib.pyplot as plt
import math
from alipy import ToolBox
# python3.9以上版本需要加上
import collections

collections.Iterable = collections.abc.Iterable

# config
BATCH_SIZE = 256  # batch size
HIDDEN_SIZE = 100  # 隐层维度
N_LAYER = 2  # RNN层数
N_EPOCHS = 100  # 训练轮数
N_CHARS = 128  # 字符
USE_GPU = True  # 是否使用gpu
performance_metric = 'accuracy_score'  # alipy box的性能指标
# device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
device = torch.device('cuda:0')  # 使用gpu
learning_rate = 0.001  # 学习率
stopping_criterion = 'num_of_queries'  # 停止策略
num_of_queries = 10  # 如果停止策略是num_of_queries,则设置查询次数
test_ratio = 0.1  # 测试集的比例
initial_label_rate = 0.4  # 初始化被标记实例的比例
split_count = 15  # 将训练集划分出多少个初始化被标记集合
query_batch_size = 10  # 查询策略每次查询的实例数
query_type = 'AllLabels'  # 查询类型
saving_path = '.'  # 保存路径
train_file = 'data/names_train.csv'
test_file = 'data/names_test.csv'
dev_acc_list = []


# prepare data
class NameDataset(Dataset):
    def __init__(self, is_train_set=True):
        filename = 'data/names_train.csv' if is_train_set else 'data/names_test.csv'
        data = pd.read_csv(filename, delimiter=',', names=['names', 'country'])
        self.names = data['names']
        self.len = len(self.names)
        self.countries = data['country']
        self.countries_list = list(sorted(set(self.countries)))
        self.countries_dict = self.getCountryDict()
        self.countries_num = len(self.countries_list)

    def __getitem__(self, item):
        return self.names[item], self.countries_dict[self.countries[item]]

    def __len__(self):
        return self.len

    def getCountryDict(self):
        country_dict = {}
        for idx, country in enumerate(self.countries_list, 0):
            country_dict[country] = idx
        return country_dict

    def id2country(self, idx):
        return self.countries[idx]

    def getCountryNum(self):
        return self.countries_num


# 主动学习训练集
class ALDataset(Dataset):
    def __init__(self, names, countries):
        self.names = names
        self.countries = countries
        self.countries_list = list(sorted(set(self.countries)))
        self.countries_dict = self.getCountryDict()
        self.countries_num = len(self.countries_list)

    def __getitem__(self, item):
        return self.names[item], self.countries_dict[self.countries[item]]

    def __len__(self):
        assert len(self.names) == len(self.countries)
        return len(self.names)

    def getCountryDict(self):
        country_dict = {}
        for idx, country in enumerate(self.countries_list, 0):
            country_dict[country] = idx
        return country_dict

    def update(self, names, countries):
        self.names = np.append(self.names, names)
        self.countries = np.append(self.countries, countries)
        self.countries_list = list(sorted(set(self.countries)))
        self.countries_dict = self.getCountryDict()
        self.countries_num = len(self.countries_list)


# 训练集
train_data = NameDataset(is_train_set=True)
# trainloader = DataLoader(train_data, shuffle=True)
# 测试集
test_data = NameDataset(is_train_set=False)
init_testloader = DataLoader(test_data, shuffle=False)
train_names = list(train_data.names)
train_countries = list(train_data.countries)
N_COUNTRY = train_data.getCountryNum()  # 国家的数量


# 模型
class RNNClassifier(torch.nn.Module):
    def __init__(self, input_size, hidden_size, output_size, n_layer=1, bidirectional=True):
        super(RNNClassifier, self).__init__()
        self.hidden_size = hidden_size
        self.n_layer = n_layer
        self.n_directions = 2 if bidirectional else 1

        self.emb = torch.nn.Embedding(input_size, hidden_size)
        self.gru = torch.nn.GRU(hidden_size, hidden_size, num_layers=n_layer,
                                bidirectional=bidirectional)
        self.fc = torch.nn.Linear(hidden_size * self.n_directions, output_size)

    def forward(self, inputs, seq_lengths):
        inputs = create_tensor(inputs.t())
        batch_size = inputs.size(1)

        hidden = self._init_hidden(batch_size)
        embedding = self.emb(inputs)

        gru_input = torch.nn.utils.rnn.pack_padded_sequence(embedding, seq_lengths, enforce_sorted=False)  # 用于提速

        output, hidden = self.gru(gru_input, hidden)
        if self.n_directions == 2:
            # 如果是双向神经网络,则有两个hidden,需要将它们拼接起来
            hidden_cat = torch.cat([hidden[-1], hidden[-2]], dim=1)
        else:
            hidden_cat = hidden[-1]
        fc_output = self.fc(hidden_cat)
        return fc_output

    def _init_hidden(self, batch_size):
        hidden = torch.zeros(self.n_layer * self.n_directions, batch_size, self.hidden_size)
        return create_tensor(hidden)


def create_tensor(tensor):
    if USE_GPU:
        device = torch.device('cuda:0')
        tensor = tensor.to(device)
    return tensor


def make_tensors(names, countries):
    sequences_and_lengths = [name2list(name) for name in names]  # 得到name所有字符的ASCII码值和name的长度
    name_sequences = [sl[0] for sl in sequences_and_lengths]  # 获取name中所有字符的ASCII码值
    seq_lengths = torch.LongTensor([sl[1] for sl in sequences_and_lengths])  # 获取所有name的长度
    # 获得所有name的tensor,形状 batch_size*max(seq_len)  即name的个数*最长的name的长度
    seq_tensor = torch.zeros(len(name_sequences), seq_lengths.max()).long()  # 形状[name的个数*最长的name的长度]
    for idx, (seq, seq_len) in enumerate(zip(name_sequences, seq_lengths), 0):
        seq_tensor[idx, :seq_len] = torch.LongTensor(seq)  # 将所有name逐行填充到seq_tensor中

    #   sort by length to use pack_padded_sequence
    seq_lengths, perm_idx = seq_lengths.sort(dim=0, descending=True)  # 将seq_lengths按降序排列,perm_idx是排序后的序号
    seq_tensor = seq_tensor[perm_idx]  # seq_tensor中的顺序也随之改变
    countries = countries[perm_idx]  # countries中的顺序也随之改变

    # 返回所有names转为ASCII码的tensor,所有names的长度的tensor,所有country的tensor
    return seq_tensor, \
        seq_lengths, \
        countries


def name2list(name):
    arr = [ord(c) for c in name]  # 将string转为list且所有字符转为ASCII码值
    return arr, len(arr)  # 返回的是tuple([arr],len(arr))


def main_loop(alibox, strategy, round):
    # Get the data split of one fold experiment
    # 对实验数据进行拆分
    train_idx, test_idx, label_ind, unlab_ind = alibox.get_split(round)
    # Get intermediate results saver for one fold experiment
    # 获取StateIO对象
    saver = alibox.get_stateio(round)
    # 获取训练集al_traindata
    al_traindata = ALDataset(np.array(train_names)[label_ind], np.array(train_countries)[label_ind])
    # 测试
    test_inputs = X[test_idx].to(device)
    test_lengths = seq_lengths[test_idx]
    test_targets = y[test_idx].to(device)
    pred = model(test_inputs, test_lengths).max(dim=1, keepdim=True)[1]
    # 计算准确率
    accuracy = alibox.calc_performance_metric(y_true=test_targets.to('cpu'),
                                              y_pred=pred.to('cpu'),
                                              performance_metric=performance_metric)

    # 保存参数
    saver.set_initial_point(accuracy)
    # If the stopping criterion is simple, such as query 50 times. Use `for i in range(50):` is ok.
    total_loss = 0.0
    while not stopping_criterion.is_stop():
        # Select a subset of Uind according to the query strategy
        # Passing model=None to use the default model for evaluating the committees' disagreement
        select_ind = strategy.select(label_index=label_ind, unlabel_index=unlab_ind,
                                     batch_size=query_batch_size)
        label_ind.update(select_ind)
        unlab_ind.difference_update(select_ind)
        # 获得初始更新al_traindata
        al_traindata.update(np.array(train_names)[select_ind], np.array(train_countries)[select_ind])
        al_trainloader = DataLoader(al_traindata, batch_size=BATCH_SIZE, shuffle=True)
        # 训练模型
        modelTrain(al_trainloader)
        # 测试
        model.eval()
        with torch.no_grad():
            test_inputs = X[test_idx].to(device)
            test_lengths = seq_lengths[test_idx]
            test_targets = y[test_idx].to(device)
            pred = model(test_inputs, test_lengths).max(dim=1, keepdim=True)[1]
            # 计算准确率
            accuracy = alibox.calc_performance_metric(y_true=test_targets.to('cpu'),
                                                      y_pred=pred.to('cpu'),
                                                      performance_metric=performance_metric)
        # Save intermediate results to file
        st = alibox.State(select_index=select_ind, performance=accuracy)
        saver.add_state(st)

        # Passing the current progress to stopping criterion object
        stopping_criterion.update_information(saver)
    # Reset the progress in stopping criterion object
    print('loss: %.4f, accuracy: %.4f' % (total_loss / float(stopping_criterion.value), accuracy))
    stopping_criterion.reset()
    return saver


def active_learning(alibox):
    unc_result = []
    qbc_result = []
    eer_result = []
    quire_result = []
    density_result = []
    bmdr_result = []
    spal_result = []
    lal_result = []
    rnd_result = []

    _I_have_installed_the_cvxpy = False

    for round in range(split_count):
        train_idx, test_idx, label_ind, unlab_ind = alibox.get_split(round)
        # Use pre-defined strategy

        # 获得初始trainloader和testloader
        al_traindata = ALDataset(np.array(train_names)[label_ind], np.array(train_countries)[label_ind])
        al_trainloader = DataLoader(al_traindata, batch_size=BATCH_SIZE, shuffle=True)
        al_testdata = ALDataset(np.array(train_names)[test_idx], np.array(train_countries)[test_idx])
        al_testloader = DataLoader(al_testdata, batch_size=BATCH_SIZE, shuffle=False)
        # 训练模型
        loss = modelTrain(al_trainloader)
        print('loss:', loss / (al_traindata.__len__() / BATCH_SIZE).__ceil__())
        modelTest(al_testloader)

        unc = alibox.get_query_strategy(strategy_name="QueryInstanceUncertainty")
        qbc = alibox.get_query_strategy(strategy_name="QueryInstanceQBC")
        # eer = alibox.get_query_strategy(strategy_name="QueryExpectedErrorReduction")
        rnd = alibox.get_query_strategy(strategy_name="QueryInstanceRandom")
        # quire = alibox.get_query_strategy(strategy_name="QueryInstanceQUIRE", train_idx=train_idx)
        density = alibox.get_query_strategy(strategy_name="QueryInstanceGraphDensity", train_idx=train_idx)
        # lal = alibox.get_query_strategy(strategy_name="QueryInstanceLAL", cls_est=10, train_slt=False)
        # lal.download_data()
        # lal.train_selector_from_file(reg_est=30, reg_depth=5)
        unc_result.append(copy.deepcopy(main_loop(alibox, unc, round)))
        qbc_result.append(copy.deepcopy(main_loop(alibox, qbc, round)))
        # eer_result.append(copy.deepcopy(main_loop(alibox, eer, round)))
        rnd_result.append(copy.deepcopy(main_loop(alibox, rnd, round)))
        # quire_result.append(copy.deepcopy(main_loop(alibox, quire, round)))
        density_result.append(copy.deepcopy(main_loop(alibox, density, round)))
        # lal_result.append(copy.deepcopy(main_loop(alibox, lal, round)))

        if _I_have_installed_the_cvxpy:
            bmdr = alibox.get_query_strategy(strategy_name="QueryInstanceBMDR", kernel='rbf')
            spal = alibox.get_query_strategy(strategy_name="QueryInstanceSPAL", kernel='rbf')

            bmdr_result.append(copy.deepcopy(main_loop(alibox, bmdr, round)))
            spal_result.append(copy.deepcopy(main_loop(alibox, spal, round)))
        dev_acc_list.append(modelTest(init_testloader))
    analyser = alibox.get_experiment_analyser(x_axis='num_of_queries')
    analyser.add_method(method_name='Unc', method_results=unc_result)
    analyser.add_method(method_name='QBC', method_results=qbc_result)
    # analyser.add_method(method_name='EER', method_results=eer_result)
    analyser.add_method(method_name='Random', method_results=rnd_result)
    # analyser.add_method(method_name='QUIRE', method_results=quire_result)
    analyser.add_method(method_name='Density', method_results=density_result)
    # analyser.add_method(method_name='LAL', method_results=lal_result)
    if _I_have_installed_the_cvxpy:
        analyser.add_method(method_name='BMDR', method_results=bmdr_result)
        analyser.add_method(method_name='SPAL', method_results=spal_result)
    print(analyser)
    analyser.plot_learning_curves(title='Example of alipy', std_area=False)


def modelTrain(trainloader):
    model.train()
    total_loss = 0.0
    for i, (names, countries) in enumerate(trainloader, 1):
        inputs, seq_lengths, targets = make_tensors(names, countries)
        inputs = create_tensor(inputs)
        targets = create_tensor(targets)
        output = model(inputs, seq_lengths.to('cpu'))
        loss = criterion(output, targets)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    return total_loss  # 返回一轮训练的所有loss之和


def modelTest(testloader):
    correct = 0
    total = len(testloader.dataset.names)
    #total = len(test_data)
    print('evaluating trained model...')
    model.eval()
    with torch.no_grad():
        for i, (names, countries) in enumerate(testloader, 1):
            inputs, seq_lengths, targets = make_tensors(names, countries)
            inputs = inputs.to(device)
            targets = targets.to(device)
            output = model(inputs, seq_lengths.to('cpu'))
            pred = output.max(dim=1, keepdim=True)[1]
            correct += pred.eq(targets.view_as(pred)).sum().item()

        percent = '%.2f' % (100 * correct / total)
        print(f'Test set:Accuracy{correct}/{total} {percent}%')
    return correct / total


def time_since(since):
    s = time.time() - since
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)


if __name__ == '__main__':
    X_names = tuple(train_data.names)
    X_countries = torch.tensor([train_data.countries_dict[country] for country in train_data.countries])
    X, seq_lengths, y = make_tensors(X_names, X_countries)

    alibox = ToolBox(X=X, y=y, query_type=query_type, saving_path=saving_path)

    # Split data
    alibox.split_AL(test_ratio=test_ratio,
                    initial_label_rate=initial_label_rate,
                    split_count=split_count)

    # Use the default Logistic Regression classifier
    model = RNNClassifier(N_CHARS, HIDDEN_SIZE, N_COUNTRY, N_LAYER, bidirectional=True).to(device)

    # The cost budget is 50 times querying
    # 设置停止器,此处是查询50次
    stopping_criterion = alibox.get_stopping_criterion(stopping_criterion, num_of_queries)

    criterion = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), learning_rate)
    active_learning(alibox)
    plt.plot(range(1, len(dev_acc_list) + 1), dev_acc_list)
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.show()
    for i in range(len(dev_acc_list)):
        print(dev_acc_list[i])

运行结果

在这里插入图片描述
本图的准确率是在测试集上的效果(从训练集中划分出20%作为测试集)

在验证集上的准确率最高达到83%-84%,在之前的博客中,直接使用双向GRU模型,同样的数据集,准确率能达到84%左右,加上主动学习准确率反而下降了1%左右。
原因可能是因为主动学习更适合使用在少样本的数据集上,本文使用的数据集样本数量在13000+,因此直接使用深度学习的效果更佳。

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

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

相关文章

BLIP2预研笔记

0. 前言 文章是公司内部分享学习写的预研报告,里面有小部分文段是直接从网上借鉴的,侵删 1. 任务和方法历史进化: 在大模型等类似的预训练模型的方式(以包含“预训练阶段”等n阶段训练方式为特色)为主流之前&#xf…

太牛了!360大佬编写的《应急响应指导手册》火了!(PDF限时3天领取)

免责声明: 请使用者遵守《中华人民共和国网络安全法》,由于传播、利用本账号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号及作者不为此承担任何责任。 简介 这份《应急响应指导手册》&#xf…

【NPS】微软NPS配置802.1x,验证域账号,动态分配VLAN(NPS篇)

NPS简介 Network Policy Server(NPS)是微软Windows Server中的一个网络服务,它作为RADIUS服务器实现,用于集中管理网络接入请求。NPS处理对网络资源的认证、授权和审计请求,通常用于控制远程访问VPN和无线网络的接入。…

网络隔离状态下,如何可以安全高效地进行研发文件外发?

研发部门的数据传输通常需要保证数据的安全性、完整性和保密性,尤其是当涉及到公司的核心技术、产品设计、源代码等重要信息时。研发文件外发,即研发资料的外部传输,通常涉及到公司的核心技术和商业机密,因此需要采取严格的安全措…

【日常开发之FTP】Windows开启FTP、Java实现FTP文件上传下载

【日常开发之FTP】windows开启FTP、Java实现FTP文件上传下载 FTP前言FTP是什么?FTP两种模式 Windows开启FTPFTP windows 配置防火墙配置 Java部分Maven配置创建FTPClient 注意 FTP前言 FTP是什么? FTP是一个专门进行文件管理的操作服务,一般…

java后端15问!

前言 最近一位粉丝去面试一个中厂,Java后端。他说,好几道题答不上来,于是我帮忙整理了一波答案 G1收集器JVM内存划分对象进入老年代标志你在项目中用到的是哪种收集器,怎么调优的new对象的内存分布局部变量的内存分布Synchroniz…

【0day漏洞复现】中移铁通禹路由器信息泄露漏洞

0x01 阅读须知 “如棠安全的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供…

移动端自动化测试工具 Appium 之 main 启动

文章目录 一、背景二、生成xml文件2.1、创建xml方法2.2、执行主类MainTest2.3、自动生成的xml2.4、工程目录2.5、执行结果 三、命令行执行appium服务四、主方法启动类五、集成Jenkins六、总结 一、背景 Jenkins 做集成测试是不错的工具,那么UI自动化是否可以&#…

两种方法合并3dtiles(分别使用js/java)

目录 前言: 需合并的json目录 aa/tileset.json bb/tileset.json cc/tileset.json dd/tileset.json ee/tileset.json js源码: 运行命令: 生成结果: java源码: Matrix.java ThreeDTilesJoin2.java pom文件…

Rust 适合哪些场景?

目录 二、Rust 适合哪些场景? 三、Rust 社区的发展趋势如何? 四、Rust 快速搭建一个WebServer服务器 一、Rust是什么? Rust是一门赋予每个人构建可靠且高效软件能力的语言。 Rust 程序设计语言 一门帮助每个人构建可靠且高效软件的语言。…

2024年美国市场亚太游戏品牌数字广告洞察报告

来源:Sensor Tower 美国是全球最大的游戏市场之一,也是亚太游戏品牌出海的重要市场。2023年Q2至2024年Q1,美国市​场广告投放额排名前10的亚太游戏品牌,合计支出 超过7.5亿美元,环比上涨23%。 排名第一的米哈游(miHoY…

【将Maven源改为国内阿里云镜像源】

目录 一、如何配置Maven镜像源? 二、Idea中的Maven配置 ​三、项目与你本地仓库和中央仓库的联系 一、如何配置Maven镜像源? 1、打开你的Maven用户设置文件(settings.xml)。默认情况下,该文件存在于你的用户目录下的.m2文件夹中。如果你没…

内网安全-隧道技术SSHDNSICMPSMB上线通讯LinuxMac 简单总结

第126天:内网安全-隧道技术&SSH&DNS&ICMP&SMB&上线通讯Linux&Mac_内网安全-隧道技术_ssh_dns_icmp_smb_上线通讯linux_mac-CSDN博客 内网渗透—隧道技术_隧道技术csdn-CSDN博客 #SMB 隧道&通讯&上线 判断:445 通讯 上…

光伏设备制造5G智能工厂数字孪生可视化平台,推进行业数字化转型

光伏设备制造5G智能工厂数字孪生可视化平台,推进行业数字化转型。光伏设备制造5G智能工厂数字孪生可视化平台是光伏行业数字化转型的重要一环。通过数字孪生平台,光伏设备制造企业可以实现对生产过程的全面监控和智能管理,提高生产效率&#…

人工智能|推荐系统——工业界的推荐系统之涨指标

一、推荐系统的评价指标 涨指标的方法有哪些? 二、涨指标的方法:召回 2.1 改进双塔模型 2.2 Item-to-Item (I2I) 2.3 类似I2I 的模型 2.4 总结:改进召回模型 三、涨指标的方法:排序模型 3.1 精排模型的改进 3.2 粗排模型的改进 3…

宝塔面板如何删除一个站点

我们一般的网站都是PHPMySQL开发的,所以删除站点,就要先删数据库,再删网站目录 注意:一点要确保无用的再删 删除站点目录

第十二届蓝桥杯省赛真题 Java 研究生 组【原卷】

文章目录 发现宝藏【考生须知】试题 A: 卡片试题 B: 相乘试题 C: 直线试题 D: 路径试题 E : \mathrm{E}: E: 回路计数试题 F: 时间显示试题 G: 最少砝码试题 H : \mathrm{H}: H: 杨辉三角形试题 I: 双向排序试题 J:分果果 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&…

远动通讯屏,组成和功能介绍

远动通讯屏,组成和功能介绍 远动通讯屏是基于电网安全建设而投入的远方监控厂站信息、远方切除电网负荷的设备;主经是由远动装置、通讯管理机、交换机、GPS对时装置、数字通道防雷器、模拟通道防雷器、屏柜及附件等设备组成。变电站远动通讯系统是指对广…

Middle for Mac:简洁高效的文本编辑软件

追求简洁与高效?Middle for Mac将是您文本编辑的最佳选择。这款Mac平台上的文本编辑器,以其独特的魅力和实用的功能,赢得了众多用户的喜爱。 Middle注重用户体验,采用简洁直观的界面设计,让您能够迅速上手并享受高效的…

如何减少冗长变量声明的代码行数

减少冗长变量声明的代码行数有几种方法,具体取决于编程语言和上下文。以下是一些常见的技巧: 问题背景 在编写代码时,经常需要定义许多变量和参数。如果这些变量和参数过多,会导致代码行数增加,可读性降低。例如&…