普及大型语言模型:4 位量化以实现最佳 LLM 推理

原文:towardsdatascience.com/democratizing-llms-4-bit-quantization-for-optimal-llm-inference-be30cf4e0e34

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9d30e35457881730a0084b8af576c8ff.png

由作者生成的 DALL-E 3 图像

量化模型是一种技术,涉及将模型中使用的数字精度从较高精度(如 32 位浮点数)转换为较低精度(如 4 位整数)。量化是效率和精度之间的平衡,因为它可能会以模型精度略微降低为代价,因为数值精度的降低可能会影响模型表示数据细微差异的能力。

这是我从各种来源学习 LLM 得出的假设。

在本文中,我们将探讨将 Mistral-7B-Instruct-v0.2 量化为 5 位和 4 位模型的详细步骤。然后,我们将上传量化模型到 Hugging Face hub。最后,我们将加载量化模型并评估它们以及基础模型,以找出量化对 RAG 管道性能的影响。

这是否符合我的原始假设?请继续阅读。

我们为什么要量化一个模型?

量化模型的益处包括以下方面:

  • 减少内存使用:较低精度的数字需要更少的内存,这对于在内存资源有限的设备上部署模型至关重要。

  • 计算速度更快:较低精度的计算通常更快。这对于实时应用尤为重要。

  • 能源效率:降低计算和内存需求通常会导致能耗降低。

  • 网络效率:当模型在基于云的环境中应用时,具有较低精度权重的较小模型可以更有效地通过网络传输,从而减少带宽使用。

  • 硬件兼容性:许多专门硬件加速器,尤其是针对移动和边缘设备,被设计用于高效处理整数计算。将模型量化为较低精度允许它们充分利用这些硬件能力以实现最佳性能。

  • 模型隐私:量化可能会引入噪声并使模型提取更加困难,从而在某些场景中可能增强模型的安全性和隐私性。

我们如何量化一个模型?

量化模型有多种技术,如 NF4、GPTQ 和 AWQ。我们将探讨使用 GGUF 和 llama.cpp 量化 Mistral-7B-Instruct-v0.2

GGUF

GGUF,即“Georgi Gerganov Universal Format”,由 llama.cpp 团队在 2023 年 8 月引入,是一种专门设计用于存储量化大型语言模型的二进制文件格式。它是由 llama.cpp 的创造者 Georgi Gerganov 开发的,这是一个用于运行 Llama 模型推理的 C++ 库。

GGUF 提供了一种紧凑、高效且用户友好的方式来存储量化 LLM 权重。它旨在用于单文件模型部署和快速推理。它支持各种 LLM 架构和量化方案。GGUF 允许用户使用 CPU 运行 LLM,但也可以将一些层卸载到 GPU 以提高速度。通过降低模型成本、简化模型加载和保存以及使模型更易于访问和高效,它使 LLM 世俗化。

llama.cpp

llama.cpp 提供了一个轻量级且高效的 C++ 库,用于使用存储在 GGUF 格式的 LLM 运行推理。llama.cpp 的主要功能包括跨平台支持、快速推理、易于集成和 Hugging Face 兼容性。

LlamaIndex 提供了 LlamaCPP 类以与 llama-cpp-python 库集成。

GGUF 和 llama.cpp 一起提供了一个令人信服的效率、性能和用户友好性的组合,这可以显著提高您的 LLM 应用程序。

使用 GGUF 和 llama.cpp 量化 Mistral-7B

受 Maxime Labonne 的 使用 GGUF 和 llama.cpp 量化 Llama 模型 的启发,让我们探索如何使用 GGUF 和 llama.cpp 来量化 Mistral-7B-Instruct-v0.2。查看 我的 Colab 笔记本 以获取详细步骤。

第 1 步:安装 llama.cpp

让我们先通过运行以下命令安装 llama.cpp:

# Install llama.cpp
!git clone https://github.com/ggerganov/llama.cpp
!cd llama.cpp && git pull && make clean && LLAMA_CUBLAS=1 make
!pip install -r llama.cpp/requirements.txt

第 2 步:下载和量化 Mistral-7B-Instruct-v0.2

首先,我将我的 Hugging Face 令牌存储在 Colab 的“秘密”标签页中;请参阅下面的截图。将我的令牌存储在这个秘密标签页的好处是,我不会在我的笔记本中暴露令牌,并且我可以为所有我的 Colab 笔记本重用这个秘密配置。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1912cad80b040564cf12abe9bd85e719.png

请参阅下面的代码片段以登录到 Hugging Face hub:

# first, log into hugging face hub
from google.colab import userdata
from huggingface_hub import HfApi

HF_TOKEN = userdata.get("HF_TOKEN")
api = HfApi(token=HF_TOKEN)
username = api.whoami()['name']
print(username)

现在,让我们下载我们的基础模型 mistralai/Mistral-7B-Instruct-v0.2。我们将使用列表在 TheBloke/Mistral-7B-Instruct-v0.2-GGUF 模型卡片 上列出的十二种方法中的两种方法对其进行量化:

  • Q5_K_M:5 位,推荐,低质量损失。

  • Q4_K_M:4 位,推荐,提供平衡的质量。

在下载基础模型后,我们将其转换为 fp16(16 位浮点数),这是一种量化技术,可以减小模型大小并加快推理速度,同时保持合理的模型精度。

最后,我们遍历两种量化方法来量化我们的基础模型。我们为此活动调用 llama.cpp/quantize。请参阅下面的代码片段:

MODEL_ID = "mistralai/Mistral-7B-Instruct-v0.2"
QUANTIZATION_METHODS = ["q4_k_m", "q5_k_m"]

MODEL_NAME = MODEL_ID.split('/')[-1]

# Download model
!git lfs install
!git clone @huggingface.co/{MODEL_ID">@huggingface.co/{MODEL_ID">https://{username}:{HF_TOKEN}@huggingface.co/{MODEL_ID}

# Convert to fp16
fp16 = f"{MODEL_NAME}/{MODEL_NAME.lower()}.fp16.bin"
!python llama.cpp/convert.py {MODEL_NAME} --outtype f16 --outfile {fp16}

# Quantize the model 
for method in QUANTIZATION_METHODS:
    qtype = f"{MODEL_NAME}/{MODEL_NAME.lower()}.{method.upper()}.gguf"
    !./llama.cpp/quantize {fp16} {qtype} {method}

从日志中我们可以看到,量化后的模型大小与基础模型相比大幅缩小,每个量化过程大约需要 4 分钟:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/61f8e26bf3e6e404121d39b7f6c5531b.png

5 位模型量化日志

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/871d84690782d7bd8c5f23f716e68c1b.png

4 位模型的量化日志

第 3 步:运行推理以测试量化模型

现在我们有了两个量化模型,让我们通过调用 llama.cpp/main 运行一个推理测试。下面是代码片段:

import os

model_list = [file for file in os.listdir(MODEL_NAME) if "gguf" in file]
print("Available models: " + ", ".join(model_list))
prompt = input("Enter your prompt: ")
chosen_method = input("Name of the model (options: " + ", ".join(model_list) + "): ")

# Verify the chosen method is in the list
if chosen_method not in model_list:
    print("Invalid name")
else:
    qtype = f"{MODEL_NAME}/{MODEL_NAME.lower()}.{method.upper()}.gguf"
    !./llama.cpp/main -m {qtype} -n 128 --color -ngl 35 -p "{prompt}"

从我们对 4 位量化模型进行的推理测试中,得到了一些有趣的输出:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/329c13b8b31714f390063dca9615ff5d.png

第 4 步:将量化模型推送到 Hugging Face hub

现在,我们已准备好将我们的量化模型推送到 Hugging Face hub 与社区(以及我自己)分享。这一步假设你已经在 Hugging Face 上创建了账户。

首先,登录到 Hugging Face hub,然后通过在 HfApi 上调用 create_repo 函数创建一个空仓库。最后,我们通过调用 api.upload_folder 上传我们的新 gguf 文件。下面是代码片段。

!pip install -q huggingface_hub
from huggingface_hub import create_repo , HfApi
from google.colab import userdata

username = "wenqiglantz" #change to your own username

# Defined in the secrets tab in Google Colab
api = HfApi(token=userdata.get("HF_TOKEN"))

# Create empty repo
api.create_repo(
    repo_id = f"{username}/{MODEL_NAME}-GGUF",
    repo_type="model",
    exist_ok=True,
)

# Upload gguf files
api.upload_folder(
    folder_path=MODEL_NAME,
    repo_id=f"{username}/{MODEL_NAME}-GGUF",
    allow_patterns=f"*.gguf",
)

登录到 Hugging Face hub 以验证那些两个 gguf 量化模型是否已成功上传到我的账户 [wenqiglantz/Mistral-7B-Instruct-v0.2-GGUF] 下。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/786dc6b7f2e9c17a8268aff91b9b4414.png

下一步是正确填充模型卡片,添加一个 README.md 文件。我们可以模仿基础模型 mistralai/Mistral-7B-Instruct-v0.2 的 Hugging Face 仓库中的 README.md,特别是 languagetagslicense 部分。下面是我的示例 README.md,你可以按需自定义。

---
license: apache-2.0
pipeline_tag: text-generation
tags:
- finetuned
inference: false
base_model: mistralai/Mistral-7B-Instruct-v0.2
model_creator: Mistral AI_
model_name: Mistral 7B Instruct v0.2
model_type: mistral
prompt_template: '<s>[INST] {prompt} [/INST]
  '
quantized_by: wenqiglantz
---
# Mistral 7B Instruct v0.2 - GGUF

This is a quantized model for `mistralai/Mistral-7B-Instruct-v0.2`. Two quantization methods were used:
- Q5_K_M: 5-bit, preserves most of the model's performance
- Q4_K_M: 4-bit, smaller footprints, and saves more memory

<!-- description start -->
## Description

This repo contains GGUF format model files for [Mistral AI_'s Mistral 7B Instruct v0.2](https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2).

This model was quantized in Google Colab.

现在,让我们检查我们的模型在 hub 上的情况:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2dc2ac58261f846c3b46eef74c86bb59.png

我们已成功将 Mistral-7B-Instruct-v0.2 的量化模型上传到 Hugging Face hub。太棒了!😄

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5f908ebc48f5496b0322463e17838c77.png

AutoGGUF

AutoGGUF 是 Maxime Labonne 的流行 llm-course 仓库中的一个整洁的工具。这个工具实际上允许你一键将你的模型以 GGUF 格式量化!只需更改 MODEL_IDusername 字段,查看下面的截图,然后点击运行按钮来量化你的模型并将其上传到 Hugging Face hub。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/dccc7714e5d912644b1ae24fd660c6d4.png

评估量化模型

量化过程可能会略微影响模型精度——这是我的假设;具体影响有多大?我很好奇。让我们评估量化模型,看看它们与基础模型相比如何。

我们转向 EDD(评估驱动开发)方法寻求帮助。让我们使用 LlamaIndex 的评估模块。随着最新的 LlamaPack [RagEvaluatorPack](https://llamahub.ai/l/llama_packs-rag_evaluator?from=llama_packs) 的引入,评估已经大大简化。我们通过调用以下两个来自 LlamaHub 的模块来评估模型:

  • [RagEvaluatorPack](https://llamahub.ai/l/llama_packs-rag_evaluator?from=llama_packs):一个用于评估您的 RAG 管道的 LlamaPack。它接受query_enginerag_datasetjudge_llm作为输入,运行一系列评估指标,并获取您 RAG 管道的基准分数。

  • LabelledRagDataset:我们使用保罗·格雷厄姆论文数据集,这是一个基于保罗·格雷厄姆论文的标记 RAG 数据集。请参阅其数据集卡片如下。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/111c4f47c0dd0ef81b5e86610699586c.png

现在,让我们深入了解评估实现的细节。

第 1 步:评估 5 位量化模型

我们首先使用 LlamaIndex 的 LlamaCPP 包装类通过传递来自 Hugging Face hub 的 gguf 模型 URL 来定义我们的 llm,我们在上面部分上传了我们的量化模型。

对于嵌入模型,我们使用UAE-Large-V1,这是在撰写本文时在MTEB 排行榜上排名第二的模型。

# define llm by calling LlamaCPP, pass in the gguf file from hugging face hub
llm_q5 = LlamaCPP(
    model_url="https://huggingface.co/wenqiglantz/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q5_K_M.gguf"
)

# define ServiceContext
service_context_q5 = ServiceContext.from_defaults(
    llm=llm_q5,
    embed_model="local:WhereIsAI/UAE-Large-V1" 
)

然后,我们下载了 Llama 数据集和 RagEvaluatorPack。从数据集中,包使用SimpleDirectoryReader将数据加载到documents_q5中,然后从documents_q5构建VectorStoreIndex

一旦定义了query_engine,我们就构建RagEvaluatorPack并使用一行命令启动评估:rag_evaluator_pack_q5.run()。我们使用gpt-4–1106-preview(gpt-4 turbo)作为我们的评估judge_llm(请确保在您的 Colab secrets 标签页中添加您的OPENAI_API_KEY)。请参阅下面的代码片段:

# download and install RagEvaluatorPack
RagEvaluatorPack = download_llama_pack(
  "RagEvaluatorPack", "./rag_evaluator_pack"
)

# download a LabelledRagDataset from llama-hub
rag_dataset_q5, documents_q5 = download_llama_dataset(
    "PaulGrahamEssayDataset", "./paul_graham"
)

# build index from the source documents
index_q5 = VectorStoreIndex.from_documents(documents=documents_q5)

# define query engine
query_engine_q5 = index_q5.as_query_engine(service_context=service_context_q5)

# construct RagEvaluatorPack
rag_evaluator_pack_q5 = RagEvaluatorPack(
    query_engine=query_engine_q5,
    rag_dataset=rag_dataset_q5,
    judge_llm=OpenAI(temperature=0, model="gpt-4-1106-preview")
)

# run eval
benchmark_df_q5 = rag_evaluator_pack_q5.run()  
print(benchmark_df_q5)

在我的情况下,这一步花费了超过 3 小时来完成(可能由于 gpt-4 turbo 速率限制?)。但最终我们收获了一套不错的指标;请参见下面截图中的红色高亮。有一个选项可以触发包中的arun()函数以异步运行评估,然而,在我们的情况下,因为我们使用LlamaCPP为量化模型和HuggingFaceLLM为基本模型定义了我们的llm,请参见下面的第 4 步;截至本文撰写时,这两个类都没有提供异步completion端点,因此我们将保持使用run()函数调用运行评估。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/3845b4c0c6999752e843704e0525ad54.png

从上述指标中,我们了解到评估捕捉了正确性、相关性、忠实度和上下文相似度的平均分数。让我们回顾一下这些指标的含义:

  • 正确性:评估生成的答案与参考答案的相关性和正确性。

  • 相关性:衡量响应和源节点是否与查询匹配。

  • 忠实度:衡量查询引擎的响应是否与任何源节点匹配。

  • 上下文相似度:通过语义相似度评估问答系统的质量。

我们将使用这些指标与 4 位量化模型和基础模型的指标进行比较。

第 2 步:评估 4 位量化模型

现在我们为我们的 4 位量化模型定义了 llmservice_context,请看下面的代码片段:

llm_q4 = LlamaCPP(
    model_url="https://huggingface.co/wenqiglantz/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf"
)

service_context_q4 = ServiceContext.from_defaults(
    llm=llm_q4,
    embed_model="local:WhereIsAI/UAE-Large-V1"
)

与 5 位模型评估类似,我们下载了 RagEvaluatorPack 和数据集,构建索引,定义查询引擎,构建评估包,并最终启动评估器。我们得到了以下结果:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2a6748dd16ee0baf4ec7cb03ff14ce93.png

第 3 步:评估基础模型

现在轮到评估基础模型了。我们使用 LlamaIndex 的 HuggingFaceLLM 下载基础模型和嵌入模型到本地。请看下面的代码片段。

llm_base = HuggingFaceLLM(model_name="mistralai/Mistral-7B-Instruct-v0.2")

service_context_base = ServiceContext.from_defaults(
    llm=llm_base,
    embed_model="local:WhereIsAI/UAE-Large-V1"
)

这里同样:我们下载了 RagEvaluatorPack 和数据集,构建索引,定义查询引擎,构建评估包,并最终启动评估器。我们得到了以下结果:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/bf435d2c276e1c87e30305352476713f.png

关键要点

1:量化模型在尺寸上显著缩小

让我们先检查这三个模型之间的模型尺寸差异。4 位模型是最小的,仅有略超过 4GB,小于近 5GB 的 5 位模型。基础模型的尺寸是 4 位模型的超过三倍,超过 13GB。

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/4b00cc89d65221f0862953d81479b369.png

由作者使用 Excel 创建的图表

2:4 位量化模型在特定类别中优于 5 位和基础模型

让我们把三个模型的指标评估分数并排放置,并仔细检查:

https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9e0bd3d71ba849517498be78bfd6ef74.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/dec41e4d42efd887ad258a82300191af.png

由作者使用 Excel 创建的图表

有趣的发现!4 位量化模型具有最优化性能:

  • 与 5 位和基础模型相比,最佳的正确性和相关性分数。

  • 更快的推理时间(尽管我们没有能够捕捉到确切的秒或毫秒)。

  • CPU 推理,即使我们有将模型的一些层卸载到 GPU 以实现更快推理的选项,也不需要 GPU。

但我原本预期 4 位量化模型会损失一些精度,而 5 位模型应该比 4 位模型表现更好(尽管 5 位模型确实有最高的忠实度分数,但它也有最低的相关性分数,比 4 位模型的正确性分数低)。为什么我们会看到不同的结果?

深入探讨

评估的分数不会说谎。那么,我对 4 位量化模型会损失精度的假设是不正确的吗?让我们首先转向几篇论文。

这篇论文题为“The case for 4-bit precision: k-bit Inference Scaling Laws”提出了以下观点:

我们的主要发现是,对于所有测试的模型规模和模型家族,4 位参数在固定数量的模型位上产生最佳性能。

这篇论文甚至建议“默认情况下,对于 LLM 推理,使用 4 位量化,因为它提供了总模型位和零样本精度的权衡”。论文总结道:

在这里,我们进行了一项大规模研究,对 35,000 个零样本实验进行了广泛的各种 LLM 和参数规模的研究,以分析推理过程中参数数量、量化位精度和零样本精度之间的缩放行为和权衡。我们发现,4 位量化几乎在所有情况下都是最优的,以减少模型位并最大化零样本精度。

另一篇论文题为“Pareto-Optimal Quantized ResNet Is Mostly 4-bit”指出:

在使用不同精度和参数数量对 ResNet 进行实验后,我们确定 4 位和 8 位模型在 Pareto 意义上强于 bfloat16 模型,而大多数 4 位模型优于 8 位模型。

然而,另一篇论文题为“Do Emergent Abilities Exist in Quantized Large Language Models: An Empirical Study”指出:

我们的研究发现,大型模型(无论是微调还是未微调)在 4 位权重量化下可以很好地保留涌现能力。

进一步的调查帮助我理解了为什么量化 4 位模型可以优于其基模型:

  1. 计算和内存使用效率:量化通过使用低精度算术减少了模型大小并加快了推理速度。在某些情况下,这种效率可以导致更好的性能,如果原始模型受限于计算资源。

  2. 正则化效应:量化引入了一种噪声形式,可以作为正则化器,可能提高泛化能力。这可能导致在基模型中过度拟合是关注点的一些任务上表现更好。

  3. 硬件优化:某些硬件针对低精度算术进行了优化,这意味着量化模型可能运行得更高效,延迟更低,这对于实时应用至关重要。这种改进的速度可以间接地有助于提高受益于快速处理的任务的表现。

  4. 特定场景:在某些特定任务或数据集,例如我们为保罗·格雷厄姆的论文进行的 RAG 评估场景,全精度模型捕捉到的精细细节可能并不必要,量化模型可能同样或更好地捕捉到关键信息。

  5. 模型校准:将模型量化的过程涉及将其校准到低比特表示,这有时会导致发现比原始训练中找到的更优的权重配置。

总体而言,4 比特量化在模型压缩和潜在性能提升方面具有令人兴奋的潜力。然而,需要注意的是,量化通常涉及性能与模型大小/计算效率之间的权衡。在许多情况下,量化可能导致模型精度下降,尤其是对于需要高精度的复杂任务。量化模型在超越其基模型方面的成功高度依赖于具体任务、数据集的性质以及量化过程的执行情况。

摘要

在本文中,我们探讨了使用 GGUF 和 llama.cpp 对模型进行量化的方法。我们首先探讨了模型量化的好处。然后,我们深入研究了如何使用 GGUF 和 llama.cpp 将Mistral-7B-Instruct-v0.2量化为 5 比特和 4 比特模型。我们将量化模型推送到 Hugging Face hub。我们通过使用 LlamaIndex 的[RagEvaluatorPack](https://llamahub.ai/l/llama_packs-rag_evaluator?from=llama_packs)LabelledRagDataset评估量化模型及其基模型来完成实验。

我们发现,对于我们的用例,4 比特量化模型在正确性和相关性等类别中实际上优于基模型和 5 比特量化模型,而其大小不到基模型的三分之一。

GGUF 和 llama.cpp 的组合通过减少模型大小和成本、简化模型加载和保存(单文件模型部署),以及使模型更易于访问和高效,使 LLM(大型语言模型)民主化。

4 比特量化模型可以在没有复杂任务需要高精度的情况下提供最优推理,可能匹配甚至超越其基模型的性能。然而,需要注意的是,这些 4 比特模型的成功可能高度依赖于具体用例。

完整的源代码可以在我的 GitHub 仓库my GitHub repo和我的 Colab 笔记本中找到;请见下方的链接。我将量化操作保存在一个单独的笔记本中,因为它需要 GPU,而量化模型和基础模型的评估可以在 CPU 上运行。

编程愉快!

参考文献:

学生社团系统-学生社团“一站式”运营管理平台-学生社团管理系统-基于SSM的学生社团管理系统-springboot学生社团管理系统.zip-Java学生社团管理系统开发实战-源码 更多学生社团系统: SpringBoot+Vue学生社团“一站式”运营管理平台源码(活动管理+成员考核+经费审批) Java学生社团管理系统开发实战:SSM升级SpringBoot(招新报名+场地预约+数据看板) 基于SpringSecurity的社团管理APP(移动端签到+权限分级+消息推送) 企业级社团数字化平台解决方案(SpringBoot+Redis缓存+Elasticsearch活动搜索) 微信小程序社团服务系统开发(活动直播+社团文化墙+成员互动社区) SpringBoot社团核心源码(多角色支持+工作流引擎+API接口开放) AI赋能社团管理:智能匹配兴趣标签+活动热度预测+成员贡献度分析(附代码) 响应式社团管理平台开发(PC/移动端适配+暗黑模式+无障碍访问) 完整学生社团系统源码下载(SpringBoot3+Vue3+MySQL8+Docker部署) 高校垂直领域社团平台:百团大战系统+社团星级评定+跨校活动联盟 适用对象:本代码学习资料适用于计算机、电子信息工程、数学等专业正在做毕设的学生,需要项目实战练习的学习者,也适用于课程设计、期末大作业。 技术栈:前端是vue,后端是springboot,项目代码都经过严格调试,代码没有任何bug! 核心管理:社团注册、成员管理、权限分级 活动运营:活动发布、报名签到、场地预约 资源服务:经费申请、物资管理、文档共享 数据分析:成员活跃度、活动效果评估、社团影响力排名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值