当前位置: 首页 > news >正文

深度神经网络语言识别

「AI秘籍」系列课程:

  • 人工智能应用数学基础
  • 人工智能Python基础
  • 人工智能基础核心知识
  • 人工智能BI核心知识
  • 人工智能CV核心知识

使用 DNN 和字符 n-gram 对一段文本的语言进行分类(附 Python 代码)

在这里插入图片描述

资料来源,flaticon:https://www.flaticon.com/premium-icon/cyborg_901032

语言识别是自然语言处理 (NLP) 问题中的一个重要步骤。它涉及尝试预测一段文本的自然语言。在采取其他操作(即翻译/情感分析)之前,了解文本的语言非常重要。例如,如果你使用谷歌翻译,你输入的框会显示“检测语言”。这是因为谷歌首先尝试识别你的句子的语言,然后才能翻译它。

在这里插入图片描述

语言识别有几种不同的方法,在本文中,我们将详细探讨其中一种方法。即使用神经网络和字符 n-gram 作为特征。最后,我们表明这种方法可以实现超过 98% 的准确率。在此过程中,我们将讨论关键代码,你可以在GitHub1找到完整的项目。首先,我们将讨论用于训练神经网络的数据集。

数据集

数据集2由 Tatoeba 提供。 完整数据集包含 328 种独特语言的 6,872,356 个句子。为了简化我们的问题,我们将考虑:

  • 6 种拉丁语言:英语、德语、西班牙语、法语、葡萄牙语和意大利语。
  • 长度在 20 到 200 个字符之间的句子。

我们可以在表 1 中看到每种语言的一个句子示例。我们的目标是创建一个可以使用提供的文本预测目标变量的模型。

在这里插入图片描述

我们在下面的代码中加载数据集并进行一些初始处理。我们首先过滤数据集以获取所需长度和语言的句子。我们从每种语言中随机选择 50,000 个句子,这样我们总共有 300,000 行。然后将这些句子分成训练集(70%)、验证集(20%)和测试集(10%)。

# read in full dataset
data = pd.read_csv(data_path + '/public_articles/sentences.csv', sep='\t', encoding='utf8', index_col=0,names=['lang','text'])# Filter by text length
data = data[data['text'].str.len().between(20, 200)]# Filter by text language
lang = ['deu', 'eng', 'fra', 'ita', 'por', 'spa']
data = data[data['lang'].isin(lang)]# Select 50000 rows for each language
data_trim_list = [data[data['lang'] == l].sample(50000, random_state=100) for l in lang]# Concatenate all the samples
data_trim = pd.concat(data_trim_list)# Create a random train, valid, test split
data_shuffle = data_trim.sample(frac=1, random_state=100)train = data_shuffle[:210000]
valid = data_shuffle[210000:270000]
test = data_shuffle[270000:300000]# Check the shapes to ensure everything is correct
print(f"Train set shape: {train.shape}")
print(f"Validation set shape: {valid.shape}")
print(f"Test set shape: {test.shape}")

特征工程

在拟合模型之前,我们必须将数据集转换为神经网络可以理解的形式。换句话说,我们需要从句子列表中提取特征来创建特征矩阵。我们使用字符 n-gram(n 个连续字符的集合)来实现这一点。这是一种类似于词袋模型的方法,只不过我们使用的是字符而不是单词。

对于我们的语言识别问题,我们将使用字符 3-grams/ trigrams (即 3 个连续字符的集合)。在图 2 中,我们看到了如何使用 trigrams 对句子进行矢量化的示例。首先,我们从句子中获取所有 trigrams 。为了减少特征空间,我们取这些 trigrams 的子集。我们使用这个子集对句子进行矢量化。第一个句子的向量是 [2,0,1,0,0],因为 trigrams “is_”在句子中出现两次,“his”出现一次。

在这里插入图片描述

创建三元特征矩阵的过程类似,但稍微复杂一些。在下一节中,我们将深入研究用于创建矩阵的代码。在此之前,有必要对如何创建特征矩阵进行总体概述。所采取的步骤如下:

  1. 使用训练集,我们从每种语言中选择了 200 个最常见的三字母组
  2. 根据这些 trigrams 创建一个唯一 trigrams 列表。这些语言共享一些共同的 trigrams ,因此我们最终得到了 661 个唯一 trigrams
  3. 通过计算每个句子中每个 trigrams 出现的次数来创建特征矩阵

我们可以在表 2 中看到此类特征矩阵的示例。顶行给出了 661 个 trigrams 中的每一个。然后,每个编号行给出了我们数据集中的一个句子。矩阵中的数字给出了该 trigrams 在句子中出现的次数。例如,“eux”在句子 2 中出现了一次。

表 2:训练特征矩阵

创建特征

在本节中,我们将介绍用于创建表 2 中的训练特征矩阵和验证/测试特征矩阵的代码。我们大量使用了SciKit Learn 提供的CountVectorizer包。此包允许我们根据一些词汇表(即单词/字符列表)对文本进行矢量化。在我们的例子中,词汇表是一组 661 个 trigrams 。

首先,我们必须创建这个词汇表。我们首先从每种语言中获取 200 个最常见的 trigrams 。这是使用下面代码中的*get_trigrams*函数完成的。此函数获取一个句子列表,并将从这些句子中返回 200 个最常见的 trigrams 的列表。

from sklearn.feature_extraction.text import CountVectorizerdef get_trigrams(corpus, n_feat=200):"""Returns a list of the N most common character trigrams from a list of sentencesparams------------corpus: list of stringsn_feat: integer"""# fit the n-gram modelvectorizer = CountVectorizer(analyzer='char', ngram_range=(3, 3), max_features=n_feat)X = vectorizer.fit_transform(corpus)# Get model feature namesfeature_names = vectorizer.get_feature_names_out()return feature_names

在下面的代码中,我们循环遍历这 6 种语言。对于每种语言,我们从训练集中获取相关句子。然后我们使用get_trigrams函数获取 200 个最常见的 trigrams 并将它们添加到集合中。最后,由于这些语言共享一些共同的 trigrams ,我们得到了一组 661 个独特的 trigrams 。我们用它们来创建一个词汇表。

# obtain trigrams from each language
features = {}
features_set = set()for l in lang:# get corpus filtered by languagecorpus = train[train.lang==l]['text']# get 200 most frequent trigramstrigrams = get_trigrams(corpus)# add to dict and setfeatures[l] = trigrams features_set.update(trigrams)# create vocabulary list using feature set
vocab = dict()
for i,f in enumerate(features_set):vocab[f]=i

然后,CountVectorisor 包使用词汇表对训练集中的每个句子进行矢量化。结果就是我们之前看到的表 2 中的特征矩阵。

# train count vectoriser using vocabulary
vectorizer = CountVectorizer(analyzer='char',ngram_range=(3, 3),vocabulary=vocab)# create feature matrix for training set
corpus = train['text']   
X = vectorizer.fit_transform(corpus)
feature_names = vectorizer.get_feature_names_out()train_feat = pd.DataFrame(data=X.toarray(),columns=feature_names)

在训练模型之前,最后一步是缩放特征矩阵。这将有助于我们的神经网络收敛到最佳参数权重。在下面的代码中,我们使用最小-最大缩放来缩放训练矩阵。

# Scale feature matrix 
train_min = train_feat.min()
train_max = train_feat.max()
train_feat = (train_feat - train_min)/(train_max-train_min)# Add target variable 
train_feat['lang'] = list(train['lang'])

我们还需要获取验证和测试数据集的特征矩阵。在下面的代码中,我们像对训练集所做的那样对 2 个集合进行矢量化和缩放。值得注意的是,我们使用了词汇表以及从训练集中获得的最小/最大值。这是为了避免任何数据泄露。

# create feature matrix for validation set
corpus = valid['text']   
X = vectorizer.fit_transform(corpus)valid_feat = pd.DataFrame(data=X.toarray(),columns=feature_names)
valid_feat = (valid_feat - train_min)/(train_max-train_min)
valid_feat['lang'] = list(valid['lang'])# create feature matrix for test set
corpus = test['text']   
X = vectorizer.fit_transform(corpus)test_feat = pd.DataFrame(data=X.toarray(),columns=feature_names)
test_feat = (test_feat - train_min)/(train_max-train_min)
test_feat['lang'] = list(test['lang'])

探索 trigrams

现在,我们已经准备好了可用于训练神经网络的数据集。在此之前,探索数据集并建立一些直觉来了解这些特征在预测语言方面的表现会很有用。图 2 给出了每种语言与其他语言共有的 trigrams 数量。例如,英语和德语有 56 个最常见的 trigrams 是共同的。

我们发现西班牙语和葡萄牙语的共同 trigrams 最多,有 128 个共同的 trigrams。这是有道理的,因为在所有语言中,这两种语言在词汇上最相似。这意味着,使用这些特征,我们的模型可能很难区分西班牙语和葡萄牙语,反之亦然。同样,葡萄牙语和德语的共同 trigrams 最少,我们可以预期我们的模型在区分这些语言方面会更好。

图 2: trigrams 特征相似度图

建模

我们使用keras包来训练 DNN。模型的输出层使用 softmax 激活函数。这意味着我们必须将目标变量列表转换为 one-hot 编码列表。这可以通过下面的编码函数来实现。 该函数接收目标变量列表,并返回单次编码向量列表。 例如,[eng,por,por, fra,…] 将变为[[0,1,0,0,0,0],[0,0,0,0,1,0],[0,0,0,0,1,0],[0,0,1,0,0,0],…]。

from sklearn.preprocessing import LabelEncoder
from keras.utils import np_utils# Fit encoder
encoder = LabelEncoder()
encoder.fit(['deu', 'eng', 'fra', 'ita', 'por', 'spa'])def encode(y):"""Returns a list of one hot encodings Params---------y: list of language labels"""y_encoded = encoder.transform(y)y_dummy = np_utils.to_categorical(y_encoded)return y_dummy

在选择最终模型结构之前,我进行了一些超参数调整。我改变了隐藏层中的节点数、epoch 数和批处理大小。最终模型选择了在验证集上实现最高准确率的超参数组合。

最终模型有 3 个隐藏层,分别有 500、500 和 250 个节点。输出层有 6 个节点,每个语言一个。隐藏层都具有 ReLU 激活函数,并且如上所述,输出层具有 softmax 激活函数。我们使用 4 个 epoch 和 100 的批处理大小来训练此模型。使用我们的训练集和独热编码目标变量列表,我们在以下代码中训练此 DDN。最终,我们实现了 99.57% 的训练准确率。

from keras.models import Sequential
from keras.layers import Dense#Get training data
x = train_feat.drop('lang',axis=1)
y = encode(train_feat['lang'])#Define model
model = Sequential()
model.add(Dense(500, input_dim=661, activation='relu'))
model.add(Dense(500, activation='relu'))
model.add(Dense(250, activation='relu'))
model.add(Dense(6, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])#Train model
model.fit(x, y, epochs=4, batch_size=100)

模型评估

在模型训练过程中,模型可能会偏向训练集和验证集。因此,最好在未见过的测试集上确定模型准确率。测试集的最终准确率为 98.60%。这低于训练准确率 99.57%,表明发生了一些对训练集的过度拟合。

通过查看图 3 中的混淆矩阵,我们可以更好地了解模型对每种语言的表现。红色对角线表示每种语言的正确预测数。非对角线数字表示一种语言被错误预测为另一种语言的次数。例如,德语被错误预测为英语 5 次。我们发现,该模型最常将葡萄牙语混淆为西班牙语(78 次)或将西班牙语混淆为葡萄牙语(88 次)。这是我们在探索特征时看到的结果。

图 3:困惑热图

创建此混淆矩阵的代码如下所示。首先,我们使用上面训练的模型对测试集进行预测。使用这些预测语言和实际语言,我们创建一个混淆矩阵并使用 seaborn 热图对其进行可视化。

import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import accuracy_score, confusion_matrix
import numpy as np# x_test 和 y_test 已经定义,并且 model 是一个已训练好的 Keras 模型
x_test = test_feat.drop('lang', axis=1)
y_test = test_feat['lang']# Use model.predict to get probabilities
predictions_prob = model.predict(x_test)
# Find the index of the highest probability for each sample
labels = np.argmax(predictions_prob, axis=1)
predictions = encoder.inverse_transform(labels)# Ensure y_test is a 1D array
if y_test.ndim > 1:y_test = np.argmax(y_test, axis=1)# Accuracy on test set
accuracy = accuracy_score(y_test, predictions)
print(f"Accuracy: {accuracy}")# Create confusion matrix
lang = ['deu', 'eng', 'fra', 'ita', 'por', 'spa']
conf_matrix = confusion_matrix(y_test, predictions)
conf_matrix_df = pd.DataFrame(conf_matrix, columns=lang, index=lang)# Plot confusion matrix heatmap
plt.figure(figsize=(10, 10), facecolor='w', edgecolor='k')
sns.set(font_scale=1.5)
sns.heatmap(conf_matrix_df, cmap='coolwarm', annot=True, fmt='.5g', cbar=False)
plt.xlabel('Predicted', fontsize=22)
plt.ylabel('Actual', fontsize=22)plt.savefig('../figures/model_eval.png', format='png', dpi=150)
plt.show()

最后,98.60% 的测试准确率仍有提升空间。在特征选择方面,我们保持简单,只为每种语言选择了 200 个最常见的 trigrams 。更复杂的方法可以帮助我们区分更相似的语言。例如,我们可以选择在西班牙语中很常见但在葡萄牙语中不太常见的 trigrams ,反之亦然。我们还可以尝试不同的模型。希望这对你的语言识别实验来说是一个良好的起点。

参考


  1. 茶桁的公开文章项目文件 https://github.com/hivandu/public_articles ↩︎

  2. Tatoeba 数据集 https://downloads.tatoeba.org/exports/ ↩︎

相关文章:

深度神经网络语言识别

「AI秘籍」系列课程: 人工智能应用数学基础人工智能Python基础人工智能基础核心知识人工智能BI核心知识人工智能CV核心知识 使用 DNN 和字符 n-gram 对一段文本的语言进行分类(附 Python 代码) 资料来源,flaticon:htt…...

STM32自己从零开始实操07:电机电路原理图

一、LC滤波电路 其实以下的滤波都可以叫低通滤波器。 1.1倒 “L” 型 LC 滤波电路 1.1.1定性分析 1.1.2仿真实验 电感:通低频阻高频的。仿真中高频信号通过电感,因为电感会阻止电流发生变化,故说阻止高频信号 电容:隔直通交。…...

网页计算器的实现

简介 该项目实现了一个功能完备、交互友好的网页计算器应用。只使用了 HTML、CSS 和 JavaScript ,用于检验web前端基础水平。 开发环境:Visual Studio Code开发工具:HTML5、CSS3、JavaScript实现效果 功能设计和模块划分 显示模块&#…...

JAVA设计模式-监听者模式

什么是监听者模式 监听器模式是一种观察者模式的扩展,也被称为发布-订阅模式。在监听器模式中,存在两类角色:事件源(Event Source)和监听器(Listener)。事件源负责产生事件,而监听器…...

anaconda命令大全

目录 查看所有虚拟环境查看某虚拟环境安装的包创建虚拟环境激活创建好的虚拟环境回到之前的环境删除创建的虚拟环境查看conda所在的位置、虚拟环境位置等信息conda修改虚拟环境所在的位置 查看所有虚拟环境 conda env list查看某虚拟环境安装的包 激活要查看的虚拟环境之后&a…...

“论单元测试方法及应用”写作框架,软考高级论文,系统架构设计师论文

论文真题 1、概要叙述你参与管理和开发的软件项目,以吸你所担的主要工作。 2、结给你参与管理和开发的软件项目,简要叙述单元测试中静态测试和动态测试方法的基本内容。 3、结给你惨与管理和研发的软件项目,体阐述在玩测试过程中,如何确定白盒测试的覆盖标准,及如…...

基于布雷格曼偏差校正技术的全变分一维时间序列信号降噪方法(MATLAB R2018A)

信号降噪是信号处理的重要步骤之一,目的是提高所获得信号数据的质量,以达到更高的定性和定量分析精度。信号降噪能提升信号处理其他环节的性能和人们对信息识别的准确率,给信号处理工作提供更可靠的保证。信号降噪的难点是降低噪声的同时也会…...

【CentOS 7.6】Linux版本 portainer本地镜像导入docker安装配置教程,不需要魔法拉取!(找不着镜像的来看我)

吐槽 我本来根本不想写这篇博客,但我很不解也有点生气,CSDN这么大没有人把现在需要魔法才能拉取的镜像放上来。 你们都不放,根本不方便。我来上传资源。 portainer-ce-latest.tar Linux/amd64 镜像下载地址: 链接:h…...

【windows|012】光猫、路由器、交换机详解

🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 ​ 🏅阿里云ACE认证高级工程师 ​ 🏅阿里云开发者社区专家博主 💊交流社…...

Node之Web服务

前言 本文将讲解node的web服务 通过讲解http请求,node创建web服务等知识点让你更加深入的理解web服务和node创建的web服务 HTTP请求是什么? HTTP请求是客户端(通常是浏览器或其他应用程序)与服务器之间进行通信的一种方式。 …...

[Day 24] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

AI在自動駕駛中的應用 1. 簡介 自動駕駛技術是現代交通領域的一個革命性進展。通過結合人工智能(AI)、機器學習(ML)、深度學習(DL)和傳感器技術,自動駕駛汽車可以在無人干預的情況下安全駕駛。…...

计算机图形学入门25:BRDF的测量

1.前言 BRDF(双向反射分布函数)可以用各种各样的材质去描述,但是这只是一种基于物理的描述或者近似,那什么是真正的BRDF?只有测出来的才是真正的。 为什么要测出BRDF?因为之前所描述的BRDF并不准确。如下图所示,以菲涅…...

空调计费系统是什么,你知道吗

空调计费系统是一种通过对使用空调的时间和能源消耗进行监测和计量来进行费用计算的系统。它广泛应用于各种场所,如家庭、办公室、商场等,为用户提供了方便、准确的能源使用管理和费用控制。 可实现功能 智能计费:中央空调分户计费系统通过智…...

震惊!张宇25版高数18讲发布,656页惹争议!

这个张宇老师在微博已经解释过了! 我觉得张宇老师本意是好的,在考研数学教学创新这方面,他真的有自己的思考。 他为什么要这么做? 其实作为一个考研高数老师,他完全可以像其他老师一样,什么都不做&#x…...

React+TS前台项目实战(二十三)-- 基于属性自定义数值显示组件Decimal封装

文章目录 前言Decimal组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 今天要封装的Decimal 组件,是通过传入的属性进行定制化显示数值,在渲染时,会根据不同的情况显示整数部分、小数部分和单位,支持自定义样式…...

pip install包出现哈希错误解决

如图,当遇到此类错误时,多半是连接不稳定导致的校验失败。我们可以在PC端,或Ubuntu通过浏览器下载.whl安装文件:直接复制报错信息中的网址到浏览器即可弹出下载窗口。...

多线程压测方法模板

主要步骤 创建一个线程池 ExecutorService service Executors.newFixedThreadPool(20);创建任务 Runnable task () -> {// 具体实现 };提交多个任务到线程池 for (int i 0; i < 100000; i) {service.submit(task); }关闭线程池 service.shutdown();等待所有任务完成 s…...

Uniapp软件库全新带勋章功能(包含前后端源码)

源码介绍&#xff1a; Uniapp开发的软件库全新带勋章功能&#xff0c;搭建好后台 在前端找到 util 这个文件 把两个js文件上面的填上自己的域名&#xff0c;电脑需要下载&#xff1a;HBuilderX 登录账号 没有账号就注册账号&#xff0c; 然后上传文件&#xff0c;打包选择 “…...

秋招突击——7/5——设计模式知识点补充——适配器模式、代理模式和装饰器模式

文章目录 引言正文适配器模式学习篮球翻译适配器 面试题 代理模式学习面试题 装饰器模式学习装饰模式总结 面试题 总结 引言 为了一雪前耻&#xff0c;之前腾讯面试的极其差&#xff0c;设计模式一点都不会&#xff0c;这里找了一点设计模式的面试题&#xff0c;就针对几个常考…...

bmob Harmony鸿蒙快速开发搜索功能

搜索功能是很多应用都需要的功能。在很多平台上&#xff0c;要开发一个兼容性较好的搜索功能都还是需要添加比较多的视图代码的。 为了解决这个问题&#xff0c;鸿蒙ArkUI提供了一个快速添加搜索功能的视图组件给我们&#xff0c;结合Bmob Harmony鸿蒙SDK的搜索能力&#xff0…...

软通动力子公司鸿湖万联最新成果SwanLink AI亮相世界人工智能大会

7月4日&#xff0c;2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;WAIC 2024&#xff09;在上海拉开帷幕&#xff0c;软通动力董事长兼首席执行官刘天文受邀出席开幕式。其间&#xff0c;软通动力携子公司鸿湖万联深度参与到大会各项活动中&#xff0c;并全面展…...

查看Linux系统中日志文件

Linux 系统中 Ubuntu&#xff0c;Debian&#xff0c;CentOS&#xff0c;RedHat 作为常用的服务器软件系统&#xff0c;很多人都已经熟知。不论是服务器出现问题&#xff0c;还是日常维护或各种环境搭建&#xff0c;我们经常需要登录上服务器查看日志。 如果熟知 Linux 下的 ta…...

技术干货|如何轻松完成空调管路的随机振动分析以及疲劳寿命预测

基于NVHD 的虚拟路面谱激励车内结构路噪优化 作者&#xff1a;戴相花 颜磊 吕霞 杨凯 单位&#xff1a;苏州三星电子有限公司 论文摘要 随机振动试验是评估空调管路流通振动耐久性的重要试验之一。本文以某型号空调管路系统为研究对象&#xff0c;评估空调管路随机振动疲劳…...

中英双语介绍中国的城市:上海市(Shanghai)

中文版 上海市是中国最大的城市之一&#xff0c;也是全球重要的金融、贸易和航运中心。作为一座现代化的国际大都市&#xff0c;上海以其繁华的商业区、丰富的文化遗产和多样化的经济结构而闻名。以下是对上海市的详细介绍&#xff0c;包括其地理位置、人口、经济、教育、文化…...

代码随想录算法训练营第75天:总结篇[1]

代码随想录算法训练营第75天&#xff1a;总结篇 代码随想录姑且是都过了一遍了&#xff0c;最开始了解到的时候还不会c语法&#xff0c;跟着学习还是有难度的&#xff0c;也很难坚持着去写题&#xff0c;后面决定加入训练营与大家齐头并进&#xff0c;刚开始气氛很好&#xff…...

眼动追踪技术 | 眼动的分类和模型

摘要 灵长类动物用于调整中央凹位置的正常眼动&#xff0c;几乎都可以归结为五种基本类型的组合&#xff1a;扫视、平稳追踪、聚散、前庭眼震和生理性眼震(与注视相关的微小运动)。聚散运动用于将双眼聚焦于远处的目标(深度知觉)。其他运动(如适应和聚焦)指的是眼动的非位置变…...

如何使用PHP根据输入文字动态调整图片尺寸?

根据文字改变图片尺寸的PHP代码 以下是一个使用PHP实现根据输入的文字动态调整图片尺寸的示例代码。该代码使用GD库来处理图片。 1. 创建HTML表单 首先&#xff0c;我们需要一个HTML表单来接受用户输入的文字和新的图片尺寸。 <!DOCTYPE html> <html lang"en…...

3.js - hdr贴图 是什么,有什么用

HDR贴图&#xff1a;High-Dynamic Range&#xff08;高动态范围&#xff09;贴图 一、定义与特点 定义&#xff1a;HDR贴图是具有高动态范围的环境贴图&#xff0c;能够捕捉并保留丰富的亮部细节和暗部细节&#xff0c;从而模拟出更加真实的光照效果。特点&#xff1a; 高动态…...

Nature Communications|用于电子皮肤的自主自愈晶体管(柔性半导体器件/电子皮肤/柔性电子)

2024年4月23日,韩国庆熙大学Jin Young Oh,美国斯坦福大学鲍哲南(Zhenan Bao)和韩国嘉泉大学Tae Il Lee团队,在《Nature Communications》上发布了一篇题为“Autonomous self-healing supramolecular polymer transistors for skin electronics”的论文。论文内容如下: 一…...

6月30日功能测试Day10

3.4.4拼团购测试点 功能位置&#xff1a;营销-----拼团购 后台优惠促销列表管理可以添加拼团&#xff0c;查看拼团活动&#xff0c;启动活动&#xff0c;编辑活动&#xff0c;删除活动。 可以查看拼团活动中已下单的订单以状态 需求分析 功能和添加拼团 商品拼团活动页 3…...