-
附件为出题专家组撰写的赛题介绍,以及关于第六期赛题的一些算法思路建议,对于各位选手的解题相信会有很大的启发,大家赶紧下载查看转发起来吧!以下为正文:Cloud computing has emerged as a new paradigm for on-demand access to a huge pool of computing resources. During the last decade it has formed a large-scale ecosystem of technologies, products and services which enabled the rapid innovation and development of new applications with millions of users. At the same time, cloud computing has posed new challenging problems to cloud providers that should operate the huge resource pools in a cost efficient way while providing a high quality of service to their users.To win the competition, major cloud service providers are focusing on improving the utilization of cloud resources, as this will allow to reduce the cost of cloud services. It is estimated that even a 1% increase in cloud service resource utilization can save millions of dollars in costs. The optimization of resource utilization, in turn, requires the development of advanced algorithms and technologies for cloud resource management. The virtual machine (VM) placement algorithm considered in this contest is one of these critical algorithms. This algorithm works online by finding a suitable server for running each requested user VM. The algorithm should compute its decision fast while providing a dense packing of VMs and avoiding rejections due to the lack of free space. The increase of the scale and complexity of cloud services and deployed applications has led to new user requirements. In particular, instead of a single VM, users often need to deploy a set of VMs with specific requirements on the quality of their placement. For example, to ensure high availability each VM must be placed in a separate fault domain, a set of servers that share a single point of failure, such as rack. In other situation, like neural network training, it may be preferable to place VMs "close" to each other in terms of cloud network topology to reduce the communication latency and speed up the computations.These new requirements bring new challenges to VM placement problem – how to quickly and efficiently place large sets of VMs while satisfying numerous constraints. Basic algorithms that place each VM independently cannot adapt to such complex scenarios. So the context of this Challenge is development of topology-aware VM placement algorithm that can both satisfy new constraints and ensure efficient resource utilization.The algorithm operates a large pool of servers by processing incoming user requests on creation and deletion of virtual machines. The resource pool is organized in a hierarchical topology with multiple levels. Servers live in racks with form separate fault domains, because the failure of the rack network switch or power supply leads to the failure of all servers from the rack. Racks are grouped into pods and network domains. The network communication inside the network domain is much faster than between different network domains.Cloud users create VMs from the predefined set of VM types. A user can request creation of multiple VMs of the same type at once. A user can request the deletion of any previously created VM at any time. Each VM is associated with some placement group (PG). The main role of PG is to define the requirements on placement of VMs belonging to this group. These requirements are specified as affinity and anti-affinity constraints. For example, each VM in group should be placed in a separate rack is an anti-affinity constraint. It is also possible to combine multiple constraints in a PG.The algorithm processes requests one by one, in online fashion. That means that the algorithm must output its decision regarding the current request without knowledge about the next requests. This is exactly how such algorithm runs in a real cloud. Your main goal will be to maximize the number of successfully placed VMs. As the algorithm runs on a resource pool of fixed size the more requests are allocated, the less resources are wasted and the more resource efficient is the algorithm.The first version of this problem was published as the ICPC 2022 Online Challenge powered by Huawei which attracted many algorithm enthusiasts from all over the world. For the new competition, the problem has been reworked to introduce more complex topology and placement constraints, new test cases and solution scoring. Below we describe the main differences.To model new placement constraints arising in AI training scenarios, a new topology level (pod) has been introduced which corresponds to a set of racks. Training of large AI models requires a massive amount of computations and typically runs in parallel across a large number of servers. The communication speed between these servers is crucial, therefore such workloads require allocation of one or several whole pods in a single network domain. Such constraints has been added to this challenge. We have also added more complex real-world constraints which combine affinity and anti-affinity on different topology levels.57 completely new test cases have been prepared for this challenge. While the previous challenge featured only synthetic test cases, this challenge also features test cases based on real cloud requests. The generator of synthetics cases has also been improved.Finally, the solution scoring is also reworked. While in ICPC Challenge the achieved results were compared with simple baseline algorithm, now we compare them with an upper bound on the number of successfully placed VMs which is a hard theoretical limit.The problem being solved shares many similarities with the classic bin packing and knapsack problems. Indeed, we need to pack items represented by VMs into bins represented by servers. Therefore algorithms for solving these well-known problems can be used to efficiently pack VMs into servers without leaving unused resources. However, our problem is remarkably harder than these classic problems because of the following features. First, we consider online scheduling in which future requests are unknown in advance, while most of classic algorithms consider offline setting where all packed items are given as input. Development of online algorithm requires dealing with uncertainty. One approach is to minimize the risks by preparing to challenging requests. For example, we can be prepared for upcoming requests for large VM by reserving enough free space to accommodate such VMs. Another approach is to try to anticipate future requests by using some predictive model based on already processed requests. For example, the distribution of requested VM types, or what kind or requests arrived recently, may provide some hints about the upcoming requests.Second, we consider the dynamic packing problem where the items may depart from the bins, which also quite different from the classic bin packing. While item departures are not as challenging as dynamic item arrivals and can even simplify the packing, they can also be leveraged to improve the packing efficiency. For example, by packing together VMs with similar lifetime, we can improve the availability of large free space on servers for packing large VMs. This requires predicting the VM lifetime, which can be also done using the already observed information. Note also that VMs from the same PG are usually deleted in batches, so their lifetime is similar.Third, the most complexity comes from the placement constraints. Consider a PG with affinity constraint on rack level. When placing the first request from this group, we need to choose a rack with enough free space to accommodate the VMs from this request and the VMs from upcoming requests of this group. This means that it is no longer safe to pack VMs one by one, without considering whether the whole request will fit a rack. It also means that a care should be taken to provide some room for possible future growth of the group inside the chosen rack. One possible approach is to choose a rack with the most available free space. Note also that groups cannot grow infinitely, there is some limit on their size. Anti-affinity constraints are usually less challenging but they also require the strategical management of available space to success. For example, for anti-affinity on rack level with partitions it is better to choose the racks where the request partition is already present to avoid occupying too much racks for some partition, since it will reduce possible racks for accommodating other partitions. Think about what kind of similar strategies can be applied for other constraints. Finally, when choosing a topology domain for placing VMs it is convenient to select domains in a top-down fashion using some scoring metrics or rules for each domain on some level.As a last recommendation and a word of caution, try to avoid overfitting your solution parameters and even placement strategies to individual tests. This will both complicate your algorithm and make it inapplicable in a real cloud setting. Strive for simpler and general algorithms with a clear intuition behind them and a small number of parameters, and also add comments in the code to explaining them. That is what we expect from a perfect solution to our challenge.
-
在深度学习算法中,过拟合是一个常见的问题,即模型在训练数据上表现良好,但在测试数据或实际应用中性能下降。请提出几种解决过拟合问题的有效方法,并解释其原理和应用场景。例如,可以采用正则化技术、增加数据集多样性、使用dropout等方法来防止过拟合。
-
total time limit: 50 seconds是指每个测试集50秒还是一共50秒
-
但是本地测试一些简单的样例后,结果确实是正确的。baseline有分数,但是使用一些近似检索算法后就一直得分为0。。图1是baseline,图2 是改进代码,没有调用额外的库。
-
在本地运行没有任何问题,本次初始化5ms都不到的初始化程序,在提交到系统之后一直显示”初始化超时“,求指导,该怎么办?
-
在将segformer-b0算法模型转OM后,在MDC300F MINI上推理耗时1000ms。ONNX中MatMul算子的输入数据的shape带batch维度,将算子的type类型更改为 BatchMatMul后,转OM时,发现MatMul算子前后各出现一个trans_TransData算子。通过profiling计算算子耗时,发现TransposeD、ArgMaxD、TransData、SoftmaxV2、BatchMatMul占用大量的推理时间。请问如何避免带batch维度的MatMul产生TransData算子?针对现在算子耗时,有什么优化的方法吗?
-
在算法领域中,深度优先搜索(DFS)和广度优先搜索(BFS)是两种常见且重要的图遍历算法。它们各自具有独特的特点和适用场景。本文将详细介绍这两种算法,并探讨它们的最佳应用场景。一、深度优先搜索(DFS)深度优先搜索是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索图的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。DFS的应用场景:图的连通性检查:给定一个无向图,判断是否存在从节点A到节点B的路径。 生成树的构造:在计算机网络中,DFS可以用来发现网络的拓扑结构,并生成一棵生成树。 解决迷宫问题:在迷宫问题中,DFS可以用来找到从入口到出口的路径。 树的遍历:对于二叉树、多叉树等数据结构,DFS是一种非常有效的遍历方式。二、广度优先搜索(BFS)广度优先搜索是另一种用于遍历或搜索树或图的算法。它从根(或任意节点)开始并探索最近的节点,然后进行下一层的邻居节点,这样一层一层地进行。BFS使用队列数据结构来保存信息。BFS的应用场景:最短路径问题:在图中找到从一个节点到另一个节点的最短路径。例如,在地图中找到从起点到终点的最短路线。 层次遍历:对于二叉树、多叉树等数据结构,BFS可以用来进行层次遍历。 图的连通性检查:与DFS相似,BFS也可以用来检查图的连通性,但它通常用于找到从起点到所有其他可达节点的最短路径。 网络爬虫:在网页搜索中,BFS被用来遍历网页之间的链接,从而找到与给定网页相关的所有网页。总结DFS和BFS各有优缺点,选择哪种算法取决于具体的问题和应用场景。DFS通常用于深度探索,而BFS则更适用于广度探索。在实际应用中,我们需要根据问题的特性来选择最合适的算法。希望本文能帮助您更好地理解深度优先搜索和广度优先搜索,以及它们在不同场景下的应用。
-
动画图解、一键运行的数据结构与算法教程项目地址: GitHub在线阅读 下载PDF《Hello 算法》:动画图解、一键运行的数据结构与算法教程,支持 Java, C++, Python, Go, JS, TS, C#, Swift, Rust, Dart, Zig 等语言。关于本书本项目旨在打造一本开源免费、新手友好的数据结构与算法入门教程。全书采用动画图解,内容清晰易懂、学习曲线平滑,引导初学者探索数据结构与算法的知识地图。源代码可一键运行,帮助读者在练习中提升编程技能,了解算法工作原理和数据结构底层实现。鼓励读者互助学习,提问与评论通常可在两日内得到回复。推荐语
-
雪花算法生成的ID为什么能保证全局唯一?
-
Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。其算法本身是通过改变数据分布来实现的,它根据每次训练集之中每个样本的分类是否正确,以及上次的总体分类的准确率,来确定每个样本的权值。将修改过权值的新数据集送给下层分类器进行训练,最后将每次训练得到的分类器最后融合起来,作为最后的决策分类器。使用adaboost分类器可以排除一些不必要的训练数据特征,并放在关键的训练数据上面。 AdaBoost用于短决策树。创建第一棵树之后,使用树在每个训练实例上的性能来得到一个权重,决定下一棵树对每个训练实例的注意力。 难以预测的训练数据被赋予更多权重,而易于预测的实例被赋予更少的权重。模型是一个接一个地顺序创建的,每个模型更新训练实例上的权重,这些权重影响序列中下一个树所执行的学习。构建完所有树之后,将对新数据进行预测。 因为着重于修正算法的错误,所以重要的是提前清洗好数据,去掉异常值。 Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器)。 算法应用 对adaBoost算法的研究以及应用大多集中于分类问题,同时也出现了一些在回归问题上的应用。就其应用adaBoost系列主要解决了: 两类问题、多类单标签问题、多类多标签问题、大类单标签问题、回归问题。它用全部的训练样本进行学习。 应用示例 adaboost 是 bosting 的方法之一,bosting就是把若干个分类效果并不好的分类器综合起来考虑,会得到一个效果比较好的分类器。 下图,左右两个决策树,单个看是效果不怎么好的,但是把同样的数据投入进去,把两个结果加起来考虑,就会增加可信度。 adaboost 的栗子,手写识别中,在画板上可以抓取到很多 features,例如 始点的方向,始点和终点的距离等等。 training 的时候,会得到每个 feature 的 weight,例如 2 和 3 的开头部分很像,这个 feature 对分类起到的作用很小,它的权重也就会较小。 而这个 alpha 角 就具有很强的识别性,这个 feature 的权重就会较大,最后的预测结果是综合考虑这些 feature 的结果。
-
wrf压缩版和非压缩版的区别?wrf运行的输出文件是否被压缩wrf压缩版和非压缩版的关键压缩wrf的中输出文件由netcdf处理输出,所以两者区别在于netcdf的不同。社区文档默认安装压缩版的netcdf,现在记录下非压缩版netcdf的安装压缩版netcdf-c,主要添加--disable-netcdf-4./configure --prefix=${NETCDT_DIR} --build=aarch64-unknown-linux-gnu --disable-netcdf-4 --enable-shared --enable-netcdf-4 --disable-dap --with-pic --disable-doxygen --enable-static --enable-pnetcdf --enable-largefile CPPFLAGS="-O3 -I${HDF5_DIR}/include -I${PNETCDF_DIR}/include" LDFLAGS="-L${HDF5_DIR}/lib -L${PNETCDF_DIR}/lib -Wl,-rpath=${HDF5_DIR}/lib -Wl,-rpath=${PNETCDF_DIR}/lib" CFLAGS="-O3 -L${HDF5_DIR}/lib -L${PNETCDF_DIR}/lib -I${HDF5_DIR}/include -I${PNETCDF_DIR}/include"压缩版netcdf-fortran,主要添加--disable-fortran-type-check./configure --prefix=${NETCDT_DIR} --build=aarch64-unknown-linux-gnu --disable-fortran-type-check --enable-shared --with-pic --disable-doxygen --enable-largefile --enable-static CPPFLAGS="-O3 -I${HDF5_DIR}/include -I${NETCDT_DIR}/include" LDFLAGS="-L${HDF5_DIR}/lib -L${NETCDT_DIR}/lib -Wl,-rpath=${HDF5_DIR}/lib -Wl,-rpath=${NETCDT_DIR}/lib" CFLAGS="-O3 -L${HDF5_DIR}/lib -L${NETCDT_DIR}/lib -I${HDF5_DIR}/include -I${NETCDT_DIR}/include" CXXFLAGS="-O3 -L${HDF5_DIR}/lib -L${NETCDT_DIR}/lib -I${HDF5_DIR}/include -I${NETCDT_DIR}/include" FCFLAGS="-O3 -L${HDF5_DIR}/lib -L${NETCDT_DIR}/lib -I${HDF5_DIR}/include -I${NETCDT_DIR}/include"其余安装步骤与压缩版netcdf移植。检查netcdf是否是压缩版命令行输入: nc-config --all 非压缩版如下图:压缩版如下图:判断wrf是否是压缩版:./configure 之后出现以下信息: 非压缩版: 压缩版:
-
传统意义上,很多的分类问题试图解决两类或者多类情况,机器学习应用的目标是采用训练数据,将测试数据属于哪个类进行区分。但是如果只有一类数据,而目标是测试新的数据并且检测它是否与训练数据相似。One-Class Support Vector Machine在过去20里逐渐流行,就是为了解决这样的问题。本篇博客对One-Class SVM进行介绍,而One-Class SVM也会应用到我的论文中。1. Just One Class?首先先看下我们的问题,我们希望确定新的训练数据是否属于某个特定类?怎么会有这样的应用场景呢?比方说,我们要判断一张照片里的人脸,是男性还是女性,这是个二分类问题。就是将一堆已标注了男女性别的人脸照片(假设男性是正样本,女性是负样本),提取出有区分性别的特征(假设这种能区分男女性别的特征已构建好)后,通过svm中的支持向量,找到这男女两类性别特征点的最大间隔。进而在输入一张未知性别的照片后,经过特征提取步骤,就可以通过这个训练好的svm很快得出照片内人物的性别,此时我们得出的结论,我们知道要么是男性,不是男性的话,那一定是女性。以上情况是假设,我们用于训练的样本,包括了男女两类的图片,并且两类图片的数目较为均衡。现实生活中的我们也是这样,我们只有在接触了足够多的男生女生,知道了男生女生的性别特征差异后(比方说女性一般有长头发,男性一般有胡子等等),才能准确判断出这个人到底是男是女。但如果有一个特殊的场景,比方说,有一个小和尚,他从小在寺庙长大,从来都只见过男生,没见过女生,那么对于一个男性,他能很快地基于这个男性与他之前接触的男性有类似的特征,给出这个人是男性的答案。但如果是一个女性,他会发现,这个女性与他之前所认知的男性的特征差异很大,进而会得出她不是男性的判断。但注意这里的“她不是男性”不能直接归结为她是女性。再比如,在工厂设备中,控制系统的任务是判断是是否有意外情况出现,例如产品质量过低、机器产生奇怪的震动或者温度上升等。相对来说容易收集到正常场景下的训练数据,但故障系统状态的收集示例数据可能相当昂贵,或者根本不可能。 如果可以模拟一个错误的系统状态,但必然无法保证所有的错误状态都被模拟到,从而在传统的两类问题中得到识别。2. Schölkopf的OCSVMThe Support Vector Method For Novelty Detection by Schölkopf et al. 基本上将所有的数据点与零点在特征空间分离开,并且最大化分离超平面到零点的距离。这产生一个binary函数能够获取特征空间中数据的概率密度区域。当处于训练数据点区域时,返回+1,处于其他区域返回-1.该问题的优化目标与二分类SVM略微不同,但依然很相似其中表示松弛变量, 类似于二分类SVM中的,同时:它为异常值的分数设置了一个上限(训练数据集里面被认为是异常的)是训练数据集里面做为支持向量的样例数量的下届因为这个参数的重要性,这种方法也被称为。采用Lagrange技术并且采用dot-product calculation,确定函数变为:这个方法创建了一个参数为的超平面,该超平面与特征空间中的零点距离最大,并且将零点与所有的数据点分隔开。3. SVDDThe method of Support Vector Data Description by Tax and Duin (SVDD)采用一个球形而不是平面的方法,该算法在特征空间中获得数据周围的球形边界,这个超球体的体积是最小化的,从而最小化异常点的影响。产生的超球体参数为中心和半径 ,体积 被最小化,中心 是支持向量的线性组合;跟传统SVM方法相似,可以要求所有数据点 到中心的距离严格小于 ,但同时构造一个惩罚系数为 的松弛变量,优化问题如下所示:在采用拉格朗日算子求解之后,可以判断新的数据点是否在类内,如果z到中心的距离小于或者等于半径。采用Gaussian Kernel做为两个数据点的距离函数:转自 https://zhuanlan.zhihu.com/p/32784067
-
背景介绍众所周知,人类自然语言中包含了丰富的情感色彩:表达人的情绪(如悲伤、快乐)、表达人的心情(如倦怠、忧郁)、表达人的喜好(如喜欢、讨厌)、表达人的个性特征和表达人的立场等等。情感分析在商品喜好、消费决策、舆情分析等场景中均有应用。利用机器自动分析这些情感倾向,不但有助于帮助企业了解消费者对其产品的感受,为产品改进提供依据;同时还有助于企业分析商业伙伴们的态度,以便更好地进行商业决策。被人们所熟知的情感分析任务是将一段文本分类,如分为情感极性为 正向、负向、其他的三分类问题:情感分析任务正向:表示正面积极的情感,如高兴,幸福,惊喜,期待等。负向:表示负面消极的情感,如难过,伤心,愤怒,惊恐等。其他:其他类型的情感。实际上,以上熟悉的情感分析任务是句子级情感分析任务。情感分析预训练模型SKEP近年来,大量的研究表明基于大型语料库的预训练模型(Pretrained Models, PTM)可以学习通用的语言表示,有利于下游NLP任务,同时能够避免从零开始训练模型。随着计算能力的发展,深度模型的出现(即 Transformer)和训练技巧的增强使得 PTM 不断发展,由浅变深。情感预训练模型SKEP(Sentiment Knowledge Enhanced Pre-training for Sentiment Analysis)。SKEP利用情感知识增强预训练模型, 在14项中英情感分析典型任务上全面超越SOTA,此工作已经被ACL 2020录用。SKEP是百度研究团队提出的基于情感知识增强的情感预训练算法,此算法采用无监督方法自动挖掘情感知识,然后利用情感知识构建预训练目标,从而让机器学会理解情感语义。SKEP为各类情感分析任务提供统一且强大的情感语义表示。论文地址:https://arxiv.org/abs/2005.05635百度研究团队在三个典型情感分析任务,句子级情感分类(Sentence-level Sentiment Classification),评价目标级情感分类(Aspect-level Sentiment Classification)、观点抽取(Opinion Role Labeling),共计14个中英文数据上进一步验证了情感预训练模型SKEP的效果。具体实验效果参考:https://github.com/baidu/Senta#skep情感分析任务还可以进一步分为句子级情感分析、目标级情感分析等任务。在下面章节将会详细介绍两种任务及其应用场景。句子级情感分析 &目标级情感分析本项目将详细全面介绍情感分析任务的两种子任务,句子级情感分析和目标级情感分析。记得给 PaddleNLP点个小小的 Star⭐开源不易,希望大家多多支持~GitHub地址:https://github.com/PaddlePaddle/PaddleNLPAI Studio平台后续会默认安装PaddleNLP最新版,在此之前可使用如下命令更新安装。!pip install--upgrade paddlenlp -i https://pypi.org/simple1 句子级情感分析对给定的一段文本进行情感极性分类,常用于影评分析、网络论坛舆情分析等场景。如:① 选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。包的早餐是西式的,还算丰富。服务吗,一般 1② 1 5.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错 1③ 房间太小。其他的都一般。。。。。。。。。 0其中1表示正向情感,0表示负向情感。句子级情感分析任务常用数据集ChnSenticorp数据集是公开中文情感分析常用数据集, 其为二分类数据集。PaddleNLP已经内置该数据集,一键即可加载。frompaddlenlp.datasets importload_datasettrain_ds, dev_ds, test_ds = load_dataset( "chnsenticorp", splits=[ "train", "dev", "test"])print(train_ds[ 0])print(train_ds[ 1])print(train_ds[ 2]){ 'text': '选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。 泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。 包的早餐是西式的,还算丰富。 服务吗,一般', 'label': 1, 'qid': ''}1.1 SKEP模型加载PaddleNLP已经实现了SKEP预训练模型,可以通过一行代码实现SKEP加载。句子级情感分析模型是SKEP fine-tune 文本分类常用模型SkepForSequenceClassification。其首先通过SKEP提取句子语义特征,之后将语义特征进行分类。from paddlenlp.transformers import SkepForSequenceClassification, SkepTokenizer# 指定模型名称,一键加载模型model = SkepForSequenceClassification.from_pretrained(pretrained_model_name_or_path= "skep_ernie_1.0_large_ch", num_classes=len(train_ds.label_list)) # 同样地,通过指定模型名称一键加载对应的Tokenizer,用于处理文本数据,如切分token,转token_id等。tokenizer = SkepTokenizer.from_pretrained(pretrained_model_name_or_path= "skep_ernie_1.0_large_ch")SkepForSequenceClassification可用于句子级情感分析和目标级情感分析任务。其通过预训练模型SKEP获取输入文本的表示,之后将文本表示进行分类。pretrained_model_name_or_path:模型名称。支持"skep_ernie_1.0_large_ch","skep_ernie_2.0_large_en"。"skep_ernie_1.0_large_ch":是SKEP模型在预训练ernie_1.0_large_ch基础之上在海量中文数据上继续预训练得到的中文预训练模型;"skep_ernie_2.0_large_en":是SKEP模型在预训练ernie_2.0_large_en基础之上在海量英文数据上继续预训练得到的英文预训练模型;num_classes: 数据集分类类别数。关于SKEP模型实现详细信息参考:https://github.com/PaddlePaddle/PaddleNLP/tree/develop/paddlenlp/transformers/skep1.2 数据处理同样地,我们需要将原始ChnSentiCorp数据处理成模型可以读入的数据格式。SKEP模型对中文文本处理按照字粒度进行处理,我们可以使用PaddleNLP内置的SkepTokenizer完成一键式处理。importosfromfunctools importpartialimportnumpy asnpimportpaddleimportpaddle.nn.functional asFfrompaddlenlp.data importStack, Tuple, Padfromutils importcreate_dataloaderdefconvert_example(example,tokenizer,max_seq_length= 512,is_test=False) :# 将原数据处理成model可读入的格式,enocded_inputs是一个dict,包含input_ids、token_type_ids等字段encoded_inputs = tokenizer(text=example[ "text"], max_seq_len=max_seq_length)# input_ids:对文本切分token后,在词汇表中对应的token idinput_ids = encoded_inputs[ "input_ids"]# token_type_ids:当前token属于句子1还是句子2,即上述图中表达的segment idstoken_type_ids = encoded_inputs[ "token_type_ids"]ifnotis_test:# label:情感极性类别label = np.array([example[ "label"]], dtype= "int64")returninput_ids, token_type_ids, labelelse:# qid:每条数据的编号qid = np.array([example[ "qid"]], dtype= "int64")returninput_ids, token_type_ids, qid# 批量数据大小batch_size = 32 # 文本序列最大长度max_seq_length = 256# 将数据处理成模型可读入的数据格式trans_func = partial(convert_example,tokenizer=tokenizer,max_seq_length=max_seq_length)# 将数据组成批量式数据,如# 将不同长度的文本序列padding到批量式数据中最大长度# 将每条数据label堆叠在一起batchify_fn = lambda samples, fn=Tuple(Pad(axis=0, pad_val=tokenizer.pad_token_id), # input_idsPad(axis=0, pad_val=tokenizer.pad_token_type_id), # token_type_idsStack # labels): [data for data in fn(samples)]train_data_loader = create_dataloader(train_ds,mode='train',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)dev_data_loader = create_dataloader(dev_ds,mode='dev',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)1.3 模型训练和评估定义损失函数、优化器以及评价指标后,即可开始训练。推荐超参设置:max_seq_length=256batch_size=48learning_rate=2e-5epochs=10实际运行时可以根据显存大小调整batch_size和max_seq_length大小。importtimefromutils importevaluate# 训练轮次epochs = 1# 训练过程中保存模型参数的文件夹ckpt_dir = "skep_ckpt"# len(train_data_loader)一轮训练所需要的step数num_training_steps = len(train_data_loader) * epochs# Adam优化器optimizer = paddle.optimizer.AdamW(learning_rate= 2e-5,parameters=model.parameters) # 交叉熵损失函数criterion = paddle.nn.loss.CrossEntropyLoss # accuracy评价指标metric = paddle.metric.Accuracy# 开启训练global_step = 0tic_train = time.time forepoch inrange( 1, epochs + 1):forstep, batch inenumerate(train_data_loader, start= 1):input_ids, token_type_ids, labels = batch# 喂数据给modellogits = model(input_ids, token_type_ids)# 计算损失函数值loss = criterion(logits, labels)# 预测分类概率值probs = F.softmax(logits, axis= 1)# 计算acccorrect = metric.compute(probs, labels)metric.update(correct)acc = metric.accumulateglobal_step += 1ifglobal_step % 10== 0:print("global step %d, epoch: %d, batch: %d, loss: %.5f, accu: %.5f, speed: %.2f step/s"% (global_step, epoch, step, loss, acc,10/ (time.time - tic_train)))tic_train = time.time# 反向梯度回传,更新参数loss.backwardoptimizer.stepoptimizer.clear_gradifglobal_step % 100== 0:save_dir = os.path.join(ckpt_dir, "model_%d"% global_step)ifnotos.path.exists(save_dir):os.makedirs(save_dir)# 评估当前训练的模型evaluate(model, criterion, metric, dev_data_loader)# 保存当前模型参数等model.save_pretrained(save_dir)# 保存tokenizer的词表等tokenizer.save_pretrained(save_dir)globalstep 10, epoch: 1, batch: 10, loss: 0.59440, accu: 0.60625, speed: 0.76step/sglobalstep 20, epoch: 1, batch: 20, loss: 0.42311, accu: 0.72969, speed: 0.78step/sglobalstep 30, epoch: 1, batch: 30, loss: 0.09735, accu: 0.78750, speed: 0.72step/s··········globalstep 290, epoch: 1, batch: 290, loss: 0.14512, accu: 0.92778, speed: 0.71step/sglobalstep 300, epoch: 1, batch: 300, loss: 0.12470, accu: 0.92781, speed: 0.77step/seval loss: 0.19412, accu: 0.926671.4预测提交结果使用训练得到的模型还可以对文本进行情感预测。importnumpy asnpimportpaddle# 处理测试集数据trans_func = partial(convert_example,tokenizer=tokenizer,max_seq_length=max_seq_length,is_test= True)batchify_fn = lambdasamples, fn=Tuple(Pad(axis= 0, pad_val=tokenizer.pad_token_id), # inputPad(axis= 0, pad_val=tokenizer.pad_token_type_id), # segmentStack # qid): [data fordata infn(samples)]test_data_loader = create_dataloader(test_ds,mode= 'test',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)# 根据实际运行情况,更换加载的参数路径params_path = 'skep_ckp/model_500/model_state.pdparams'ifparams_path andos.path.isfile(params_path):# 加载模型参数state_dict = paddle.load(params_path)model.set_dict(state_dict)print( "Loaded parameters from %s"% params_path)label_map = { 0: '0', 1: '1'}results = [] # 切换model模型为评估模式,关闭dropout等随机因素model.evalforbatch intest_data_loader:input_ids, token_type_ids, qids = batch# 喂数据给模型logits = model(input_ids, token_type_ids)# 预测分类probs = F.softmax(logits, axis= -1)idx = paddle.argmax(probs, axis= 1).numpyidx = idx.tolistlabels = [label_map[i] fori inidx]qids = qids.numpy.tolistresults.extend(zip(qids, labels))res_dir = "./results"ifnotos.path.exists(res_dir):os.makedirs(res_dir) # 写入预测结果with open(os.path.join(res_dir, "ChnSentiCorp.tsv"), 'w', encoding="utf8") as f:f.write( "index\tprediction\n")forqid, label inresults:f.write(str(qid[ 0])+ "\t"+label+ "\n")2 目标级情感分析在电商产品分析场景下,除了分析整体商品的情感极性外,还细化到以商品具体的“方面”为分析主体进行情感分析(aspect-level),如下:这个薯片口味有点咸,太辣了,不过口感很脆。关于薯片的口味方面是一个负向评价(咸,太辣),然而对于口感方面却是一个正向评价(很脆)。我很喜欢夏威夷,就是这边的海鲜太贵了。关于夏威夷是一个正向评价(喜欢),然而对于夏威夷的海鲜却是一个负向评价(价格太贵)。目标级情感分析任务常用数据集千言数据集已提供了许多任务常用数据集。其中情感分析数据集下载链接:https://aistudio.baidu.com/aistudio/competition/detail/50/?isFromLUGE=TRUESE-ABSA16_PHNS数据集是关于手机的目标级情感分析数据集。PaddleNLP已经内置了该数据集,加载方式,如下:train_ds, test_ds = load_dataset( "seabsa16", "phns", splits=[ "train", "test"])2.1 SKEP模型加载目标级情感分析模型同样使用SkepForSequenceClassification模型,但目标级情感分析模型的输入不单单是一个句子,而是句对。一个句子描述“评价对象方面(aspect)”,另一个句子描述"对该方面的评论"。如下图所示。# 指定模型名称一键加载模型model = SkepForSequenceClassification.from_pretrained('skep_ernie_1.0_large_ch', num_classes=len(train_ds.label_list)) # 指定模型名称一键加载tokenizertokenizer = SkepTokenizer.from_pretrained( 'skep_ernie_1.0_large_ch')2.2 数据处理同样地,我们需要将原始SE_ABSA16_PHNS数据处理成模型可以读入的数据格式。SKEP模型对中文文本处理按照字粒度进行处理,我们可以使用PaddleNLP内置的SkepTokenizer完成一键式处理。fromfunctools importpartialimportosimport timeimportnumpy asnpimportpaddleimportpaddle.nn.functional asFfrompaddlenlp.data importStack, Tuple, Paddefconvert_example(example,tokenizer,max_seq_length= 512,is_test=False,dataset_name= "chnsenticorp") :encoded_inputs = tokenizer(text=example[ "text"],text_pair=example[ "text_pair"],max_seq_len=max_seq_length)input_ids = encoded_inputs[ "input_ids"]token_type_ids = encoded_inputs[ "token_type_ids"]ifnotis_test:label = np.array([example[ "label"]], dtype= "int64")returninput_ids, token_type_ids, labelelse:returninput_ids, token_type_ids# 处理的最大文本序列长度max_seq_length= 256# 批量数据大小batch_size= 16# 将数据处理成model可读入的数据格式trans_func = partial(convert_example,tokenizer=tokenizer,max_seq_length=max_seq_length) # 将数据组成批量式数据,如# 将不同长度的文本序列padding到批量式数据中最大长度# 将每条数据label堆叠在一起batchify_fn = lambdasamples, fn=Tuple(Pad(axis= 0, pad_val=tokenizer.pad_token_id), # input_idsPad(axis= 0, pad_val=tokenizer.pad_token_type_id), # token_type_idsStack(dtype= "int64") # labels): [data fordata infn(samples)]train_data_loader = create_dataloader(train_ds,mode= 'train',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)2.3 模型训练定义损失函数、优化器以及评价指标后,即可开始训练。# 训练轮次epochs = 3# 总共需要训练的step数num_training_steps = len(train_data_loader) * epochs # 优化器optimizer = paddle.optimizer.AdamW(learning_rate= 5e-5,parameters=model.parameters) # 交叉熵损失criterion = paddle.nn.loss.CrossEntropyLoss # Accuracy评价指标metric = paddle.metric.Accuracy# 开启训练ckpt_dir = "skep_aspect"global_step = 0tic_train = time.time forepoch inrange( 1, epochs + 1):forstep, batch inenumerate(train_data_loader, start= 1):input_ids, token_type_ids, labels = batch# 喂数据给modellogits = model(input_ids, token_type_ids)# 计算损失函数值loss = criterion(logits, labels)# 预测分类概率probs = F.softmax(logits, axis= 1)# 计算acccorrect = metric.compute(probs, labels)metric.update(correct)acc = metric.accumulateglobal_step += 1ifglobal_step % 10== 0:print("global step %d, epoch: %d, batch: %d, loss: %.5f, acc: %.5f, speed: %.2f step/s"% (global_step, epoch, step, loss, acc,10/ (time.time - tic_train)))tic_train = time.time# 反向梯度回传,更新参数loss.backwardoptimizer.stepoptimizer.clear_gradifglobal_step % 100== 0:save_dir = os.path.join(ckpt_dir, "model_%d"% global_step)ifnotos.path.exists(save_dir):os.makedirs(save_dir)# 保存模型参数model.save_pretrained(save_dir)# 保存tokenizer的词表等tokenizer.save_pretrained(save_dir)2.4 模型预测使用训练得到的模型还可以对评价对象进行情感预测。@paddle.no_graddef predict(model, data_loader, label_map):model.evalresults = []forbatch indata_loader:input_ids, token_type_ids = batchlogits = model(input_ids, token_type_ids)probs = F.softmax(logits, axis= 1)idx = paddle.argmax(probs, axis= 1).numpyidx = idx.tolistlabels = [label_map[i] fori inidx]results.extend(labels)returnresults# 处理测试集数据label_map = { 0: '0', 1: '1'}trans_func = partial(convert_example,tokenizer=tokenizer,max_seq_length=max_seq_length,is_test= True)batchify_fn = lambdasamples, fn=Tuple(Pad(axis= 0, pad_val=tokenizer.pad_token_id), # input_idsPad(axis= 0, pad_val=tokenizer.pad_token_type_id), # token_type_ids): [data fordata infn(samples)]test_data_loader = create_dataloader(test_ds,mode= 'test',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)# 根据实际运行情况,更换加载的参数路径params_path = 'skep_ckpt/model_900/model_state.pdparams'ifparams_path andos.path.isfile(params_path):# 加载模型参数state_dict = paddle.load(params_path)model.set_dict(state_dict)print( "Loaded parameters from %s"% params_path)results = predict(model, test_data_loader, label_map)动手试一试是不是觉得很有趣呀。小编强烈建议初学者参考上面的代码亲手敲一遍,因为只有这样,才能加深你对代码的理解呦。本次项目对应的代码:https://aistudio.baidu.com/aistudio/projectdetail/1968542除此之外, PaddleNLP提供了多种预训练模型,可一键调用,来更换一下预训练试试吧:https://paddlenlp.readthedocs.io/zh/latest/model_zoo/transformers.html更多PaddleNLP信息,欢迎访问GitHub点star收藏后体验:https://github.com/PaddlePaddle/PaddleNLP转自 https://www.sohu.com/a/476337708_827544
-
项目场景: 在训练或者微调模型的过程中,单节点的显存溢出,或者单节点的显卡较少,算力有限。需要跨节点用多个节点多块显卡来运行这项任务。这里就需要使用分布式命令,将这项任务分布到多个节点上来处理。 问题描述 在此我仅记录我在运行分布式过程中遇到的一些问题。 首先,对于pytorch深度学习框架的分布式进程是有一套标准的流程的,简单来讲就是先通过dist进行初始化,再将模型进行分布式分配。具体所用的API为: import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP 对于预训练(或者微调)的脚本,我参考了官方文档中的测试代码,测试代码实际非常简单,搭建了一个非常小的网络,同时对其使用分布式进程,非常适合拿来做测试。链接为:官方文档 dist.init_process_group()是PyTorch中用于初始化分布式训练的函数之一。它用于设置并行训练环境,连接多个进程以进行数据和模型的分布式处理。我们通过init_process_group()函数这个方法来进行初始化,其参数包括以下内容 backend(必需参数):指定分布式后端的类型,可以是以下选项之一: ‘tcp’:使用TCP协议进行通信。 ‘gloo’:使用Gloo库进行通信。 ‘mpi’:使用MPI(Message Passing Interface)进行通信。 ‘nccl’:使用NCCL库进行通信(适用于多GPU的分布式训练)。 ‘hccl’:使用HCCL库进行通信(适用于华为昇腾AI处理器的分布式训练)。 init_method(可选参数):指定用于初始化分布式环境的方法。它可以是以下选项之一: ‘env://’:使用环境变量中指定的方法进行初始化。 ‘file:// ’:使用本地文件进行初始化。 ‘tcp:// :’:使用TCP地址和端口进行初始化。 ‘gloo:// :’:使用Gloo地址和端口进行初始化。 ‘mpi:// :’:使用MPI地址和端口进行初始化。 rank(可选参数):指定当前进程的排名(从0开始)。 world_size(可选参数):指定总共使用的进程数。 timeout(可选参数):指定初始化的超时时间。 group_name(可选参数):指定用于连接的进程组名称。 这里由于服务器采用的slurm系统,我们开始计划使用mpi去实现分布式分发,同时torch的初始化也支持mpi,原始想法是通过mpirun来进行分布式计算。但是,如果要使用mpi来实现分布式功能,必须要通过github上的源代码进行编译,通过conda和pip进行下载的pytorch自身是不携带mpi的 通过上面的参数,可以看到backend是有多重通信方式的,常用的有gloo和mpi和nccl,但是这三者是有区别的,这里我们可以参考官方文档:官方文档 这里我们直接放出结论,以供参考: 对于分布式 GPU 训练,使用 NCCL 后端。 对于分布式 CPU 训练,使用 Gloo 后端。 如果你的主机是 GPU 主机并且具有 InfiniBand 互连: 使用 NCCL,因为它是目前唯一支持 InfiniBand 和 GPUDirect 的后端。 如果你的主机是 GPU 主机并且具有以太网互连: 使用 NCCL,因为它目前提供了最好的分布式 GPU 训练性能,特别是对于多进程单节点或多节点分布式训练。 如果你遇到 NCCL 的任何问题,使用 Gloo 作为备选选项。(注意,Gloo 目前运行速度比 NCCL 慢) 如果你的主机是 CPU 主机并且具有 InfiniBand 互连: 如果你的 InfiniBand 启用了 IP over IB,使用 Gloo,否则,使用 MPI。我们计划在即将发布的版本中为 Gloo 添加 InfiniBand 支持。 如果你的主机是 CPU 主机并且具有以太网互连: 使用 Gloo,除非你有特定的理由使用 MPI。 我们可以根据文档的提示,得出,MPI是最不推荐使用的一种方法,对于英伟达的显卡,最优先的还是使用NCCL方法。和Mpi相匹配的有一种torch官方自带的方法,在torch2.0之前使用的API叫:torch.distributed.launch在使用时显示未来的版本将会弃用这个API,取而代之的是torchrun。因此我们将命令由mpi改为torchrun方法,在dist初始化使用nccl后端通信。 这里我们可以参考torchrun的官方运行方法:官方文档 经过近两周的调试与踩坑,先是在测试节点上对官方测试脚本进行分布式测试,运行成功后再将相同的环境和文件迁移到计算节点上,再在计算节点上对分布式命令进行测试,期间还测试了root用户和子用户对torchrun命令是否会有影响。 假设我们有三个节点,node02,node03,node04,每个节点上有四张GPU。现在我们将官方测试文档中的代码写为test_mpi.py。最终通过torchrun实现的命令如下: torchrun --nproc_per_node=4 --nnodes=3 --node_rank=0 --master_addr=192.168.0.101 --master_port=29500 test_mpi.py 1 我们没有必要和torchrun的官方文档一样去设置**–rdzv-backend** 和**–rdzv-id**,因为这不是必须的,用默认的即可。我们只需要设置的参数只有上面这几个。具体参数介绍如下: –nproc_per_node=4:指定每个节点(机器)上的进程数,这里是4个。意味着每个机器将启动4个进程来参与分布式训练。 –nnodes=3:指定总共的节点数,这里是3个。意味着总共有3个机器参与分布式训练。 –node_rank=0:指定当前节点(机器)的排名,这里是0。排名从0开始,用于在分布式环境中区分不同的节点。 –master_addr=192.168.0.101:指定主节点的IP地址,这里是192.168.0.101。主节点用于协调分布式训练过程。 –master_port=29500:指定主节点的端口号,这里是29500。主节点使用指定的端口来与其他节点进行通信。 通过设置这些参数,该命令将在3个节点的分布式环境中启动4个进程,并指定192.168.0.101作为主节点进行协调和通信。 这里的主节点地址我随便写的,可以根据实际情况进行修改。主节点的地址的- --node_rank必须设置为0,也就是上述这行命令,必须要先在主节点上线运行。 举个例子,假如我的主节点是node02,那么我就要先在node02节点的终端上运行上述torchrun命令,同时–master_addr要为node02的ip地址(查看IP地址可以通过:ip addr),然后node03,node04的顺序就不重要了,在其节点的终端上将–node_rank=0改为–node_rank=1和–node_rank=2运行即可。 这里出现第一个问题,即是,通讯超时(具体表现为:ERROR:torch.distributed.elastic.multiprocessing.api:failed (exitcode: -11))。假如我们的节点之前ping方法没有问题,同时节点并没有处于被占用的情况,那么分析超时就比较困难了。我会在之后的解决方法中,提供我是如何解决的。 在命令确认无误之后,我们需要将这个命令,写成脚本提交到后台,挂在服务器上运行,而不是在终端上一直在线处理。 由于我们服务器使用的slurm系统,slurm系统自带一套可以提交作业的方法。于是就要将这个命令进行sbatch脚本打包。打包的bash脚本如下所示: #!/bin/bash#SBATCH --job-name=pytorch_job # 创建一个任务名#SBATCH -N 3 # 需要使用的节点数#SBATCH --ntasks-per-node=4 # 每个节点上的任务数#SBATCH --output=job_output.out # 标准输出文件#SBATCH --error=job_error.err # 标准错误文件#SBATCH --nodelist=node02,node03,node04 # 指定节点列表# 加载任何必要的模块,例如:# module load python# module load pytorch# source ……export TORCH_DISTRIBUTED_DEBUG=INFOexport NCCL_IB_DISABLE=1# 设置主节点# 节点列表NODELIST=$(scontrol show hostname $SLURM_JOB_NODELIST)# 对第一个节点赋值为主节点MASTER_NODE=$(head -n 1 <<< "$NODELIST")# 计数器NODE_COUNT=0# 一共的节点数NODE_NUM=($(echo $NODELIST | tr " " "\n" | wc -l))# 打印echo $SLURM_NODEIDecho $NODELISTecho $MASTER_NODEecho $NODE_NUMfor NODE in $NODELIST; do if [ "$NODE" == "$MASTER_NODE" ]; then srun --nodes=1 --ntasks=1 -w $NODE torchrun --nproc_per_node=4 --nnodes=$NODE_NUM --node_rank=0 --master_addr=192.168.0.101 --master_port=29500 test_mpi.py & else ((NODE_COUNT++)) srun --nodes=1 --ntasks=1 -w $NODE torchrun --nproc_per_node=4 --nnodes=$NODE_NUM --node_rank=$NODE_COUNT --master_addr=192.168.0.101 --master_port=29500 test_mpi.py & fidonewait脚本的逻辑为:通过srun在启动的每个节点上运行torchrun命令,运行的同时还需要进行判断,判断提交的节点是不是主节点,如果是主节点则node_rank的值要为0,如果不是主节点则node_rank的值为1,2……其实并不推荐使用sbatch嵌套srun() 这里出现第二个问题,假如不是不是在主节点第一个运行命令,则会发生超时,具体情况如下: 我会在之后的解决方法中,提供我是如何解决的。 原因分析: 对于上述的两种超时问题,首先要做的是在节点之间进行ping操作确认,确认不是服务器本身的问题,则考虑是不是节点之间的通信问题。因为NCCL也是有内部通信的,NVIDIA的NCCL库支持多种传输方式,以便在不同的硬件和网络配置中实现最优的通信性能。以下是一些主要的传输方式: InfiniBand (IB):如前所述,InfiniBand是一种高性能、低延迟的网络传输技术,常用于高性能计算(HPC)和数据中心。 TCP/IP:这是最常见的网络通信协议,可以在任何支持TCP/IP的网络(包括常见的以太网)上运行。 Shared Memory (SHM):在同一台机器上的GPU之间,NCCL可以使用共享内存进行通信。这通常比通过网络传输更快。 CUDA Inter-Process Communication (IPC):对于同一节点上的多个GPU,NCCL可以使用CUDA IPC进行通信。这是一种允许不同CUDA进程共享GPU内存的机制,可以提高通信效率。 NVLink:NVLink是NVIDIA的一种高速互连技术,用于连接GPU和GPU,或GPU和CPU。它提供了比传统PCIe更高的带宽,适用于需要高速GPU间通信的应用。 这些传输方式可以根据具体的硬件配置和通信需求进行选择和配置。 解决方案: 我们可以在之前的脚本中,添加环境变量,进入调试模型,查看具体的原因: export NCCL_DEBUG=INFO export NCCL_DEBUG_SUBSYS=ALL export TORCH_DISTRIBUTED_DEBUG=INFO 1 2 3 对于第一个问题,再次运行我们的命令,即可获得NCCL的INFO信息,我们详细对比信息可以发现,在主节点上,我们使用的通信方式是NET/IB,如下图所示: 而在其他节点,我们使用的方式是 NET/Socket NET/IB 和 NET/Socket 是两种不同的网络通信接口。NET/IB 通常指的是 InfiniBand,这是一种高性能、低延迟的网络通信接口,常用于高性能计算和数据中心。而 NET/Socket 则是一种更常见的网络接口,它在各种网络环境中都可以使用。如果你的两个节点一个使用 NET/IB,另一个使用 NET/Socket,那么这两个节点之间的通信可能会受到影响。因为 NCCL 默认使用最快的可用传输方法,如果两个节点的网络接口不同,那么可能无法建立有效的通信。具体情况可能需要根据你的网络环境和配置进行测试。这里建议使用同一种通信方式。 我们将IB禁用即可: export NCCL_IB_DISABLE=1 1 对于第二个问题,我们只需要写判断语句,确保主节点运行node_rank=0的命令即可,在上述给出的代码我已经写好了判断语句。 ———————————————— 版权声明:本文为CSDN博主「萌新玉玉玉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Komach/article/details/130765773
上滑加载中
推荐直播
-
DTT年度收官盛典:华为开发者空间大咖汇,共探云端开发创新
2025/01/08 周三 16:30-18:00
Yawei 华为云开发工具和效率首席专家 Edwin 华为开发者空间产品总监
数字化转型进程持续加速,驱动着技术革新发展,华为开发者空间如何巧妙整合鸿蒙、昇腾、鲲鹏等核心资源,打破平台间的壁垒,实现跨平台协同?在科技迅猛发展的今天,开发者们如何迅速把握机遇,实现高效、创新的技术突破?DTT 年度收官盛典,将与大家共同探索华为开发者空间的创新奥秘。
去报名 -
GaussDB应用实战:手把手带你写SQL
2025/01/09 周四 16:00-18:00
Steven 华为云学堂技术讲师
本期直播将围绕数据库中常用的数据类型、数据库对象、系统函数及操作符等内容展开介绍,帮助初学者掌握SQL入门级的基础语法。同时在线手把手教你写好SQL。
去报名
热门标签